mirror of
https://github.com/github/codeql-action.git
synced 2026-05-08 23:00:26 +00:00
Compare commits
50 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bbc9e441cc | |||
| e284efba72 | |||
| 20aafcd90c | |||
| 6a1c0700c3 | |||
| 23cdd6bab6 | |||
| a2e96a4c78 | |||
| 96e7de35af | |||
| 49b2220f92 | |||
| db01c78de0 | |||
| 82388fd94a | |||
| babcc1b793 | |||
| 06df98a513 | |||
| 0ea20c5b32 | |||
| d42f654f7a | |||
| 1cc5f1d5dd | |||
| 2cc885d66e | |||
| c09500540c | |||
| 9c13fefc68 | |||
| a69f472ee9 | |||
| fbb9046bf6 | |||
| 9e6b46a9e6 | |||
| ece2addcff | |||
| 74764ac0eb | |||
| 675843d09a | |||
| 511fe43abe | |||
| 02e8dcfe9c | |||
| 68d7c5f620 | |||
| 242fd828aa | |||
| 48efe6e282 | |||
| f8c65b775d | |||
| d87945e9fd | |||
| 1644ade514 | |||
| 06687e95c8 | |||
| 9b5753ab00 | |||
| ddb83a462d | |||
| 6cee818bf3 | |||
| 41dff7fce3 | |||
| 86a804f9a7 | |||
| cbdf0df97b | |||
| f60ef170b0 | |||
| cc0733fd12 | |||
| 082575fbc3 | |||
| 1ec2fd7ea1 | |||
| 61b561867b | |||
| 4ac9009dfe | |||
| e8486b0d6c | |||
| 0cbd4b56d3 | |||
| 739e14d879 | |||
| 1d05ad7576 | |||
| 2fee1242f4 |
@@ -16,10 +16,6 @@ on:
|
||||
branches:
|
||||
- v1
|
||||
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/workflows/post-release-mergeback.yml
|
||||
|
||||
jobs:
|
||||
merge-back:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -124,4 +120,4 @@ jobs:
|
||||
--base "$BASE_BRANCH" \
|
||||
--title "$PR_TITLE" \
|
||||
--body "$PR_BODY" \
|
||||
"$DRAFT"
|
||||
${DRAFT:+"$DRAFT"} # no quotes around $DRAFT. gh will error out if there is an empty ""
|
||||
|
||||
@@ -65,20 +65,81 @@ jobs:
|
||||
shell: bash
|
||||
run: ./build.sh
|
||||
- uses: ./../action/analyze
|
||||
id: analysis
|
||||
env:
|
||||
TEST_MODE: true
|
||||
- run: |
|
||||
cd "$RUNNER_TEMP/customDbLocation"
|
||||
# List all directories as there will be precisely one directory per database
|
||||
# but there may be other files in this directory such as query suites.
|
||||
if [ "$(ls -d */ | wc -l)" != 6 ] || \
|
||||
[[ ! -d cpp ]] || \
|
||||
[[ ! -d csharp ]] || \
|
||||
[[ ! -d go ]] || \
|
||||
[[ ! -d java ]] || \
|
||||
[[ ! -d javascript ]] || \
|
||||
[[ ! -d python ]]; then
|
||||
echo "Did not find expected number of databases. Database dir contains: $(ls)"
|
||||
CPP_DB=${{ fromJson(steps.analysis.outputs.db-locations).cpp }}
|
||||
if [[ ! -d $CPP_DB ]] || [[ ! $CPP_DB == ${{ runner.temp }}/customDbLocation/* ]]; then
|
||||
echo "Did not create a database for CPP, or created it in the wrong location."
|
||||
exit 1
|
||||
fi
|
||||
CSHARP_DB=${{ fromJson(steps.analysis.outputs.db-locations).csharp }}
|
||||
if [[ ! -d $CSHARP_DB ]] || [[ ! $CSHARP_DB == ${{ runner.temp }}/customDbLocation/* ]]; then
|
||||
echo "Did not create a database for C Sharp, or created it in the wrong location."
|
||||
exit 1
|
||||
fi
|
||||
GO_DB=${{ fromJson(steps.analysis.outputs.db-locations).go }}
|
||||
if [[ ! -d $GO_DB ]] || [[ ! $GO_DB == ${{ runner.temp }}/customDbLocation/* ]]; then
|
||||
echo "Did not create a database for Go, or created it in the wrong location."
|
||||
exit 1
|
||||
fi
|
||||
JAVA_DB=${{ fromJson(steps.analysis.outputs.db-locations).java }}
|
||||
if [[ ! -d $JAVA_DB ]] || [[ ! $JAVA_DB == ${{ runner.temp }}/customDbLocation/* ]]; then
|
||||
echo "Did not create a database for Java, or created it in the wrong location."
|
||||
exit 1
|
||||
fi
|
||||
JAVASCRIPT_DB=${{ fromJson(steps.analysis.outputs.db-locations).javascript }}
|
||||
if [[ ! -d $JAVASCRIPT_DB ]] || [[ ! $JAVASCRIPT_DB == ${{ runner.temp }}/customDbLocation/* ]]; then
|
||||
echo "Did not create a database for Javascript, or created it in the wrong location."
|
||||
exit 1
|
||||
fi
|
||||
PYTHON_DB=${{ fromJson(steps.analysis.outputs.db-locations).python }}
|
||||
if [[ ! -d $PYTHON_DB ]] || [[ ! $PYTHON_DB == ${{ runner.temp }}/customDbLocation/* ]]; then
|
||||
echo "Did not create a database for Python, or created it in the wrong location."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Packaging test that runs against a javascript database
|
||||
test-packaging-javascript:
|
||||
needs: [check-js, check-node-modules]
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Move codeql-action
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir ../action
|
||||
mv * .github ../action/
|
||||
mv ../action/tests/multi-language-repo/{*,.github} .
|
||||
mv ../action/.github/workflows .github
|
||||
- uses: ./../action/init
|
||||
with:
|
||||
config-file: ".github/codeql/codeql-config-packaging.yml"
|
||||
languages: javascript
|
||||
# TODO: this is temporary until we have a release that includes the latest packaging work.
|
||||
tools: https://github.com/dsp-testing/aeisenberg-codeql-action-packaging/releases/download/codeql-bundle-20210606/codeql-bundle-linux64.tar.gz
|
||||
|
||||
- name: Build code
|
||||
shell: bash
|
||||
run: ./build.sh
|
||||
- uses: ./../action/analyze
|
||||
with:
|
||||
output: "${{ runner.temp }}/results"
|
||||
env:
|
||||
TEST_MODE: true
|
||||
- name: Assert Results
|
||||
run: |
|
||||
cd "$RUNNER_TEMP/results"
|
||||
# We should have 3 hits from these rules
|
||||
EXPECTED_RULES="javascript/example/empty-or-one-block javascript/example/empty-or-one-block javascript/example/two-block"
|
||||
|
||||
# use tr to replace newlines with spaces and xargs to trim leading and trailing whitespace
|
||||
RULES="$(cat javascript.sarif | jq -r '.runs[0].results[].ruleId' | sort | tr "\n" " " | xargs)"
|
||||
echo "Found matching rules '$RULES'"
|
||||
if [ "$RULES" != "$EXPECTED_RULES" ]; then
|
||||
echo "Did not match expected rules '$EXPECTED_RULES'."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ npm run removeNPMAbsolutePaths
|
||||
# Check that repo is still clean
|
||||
if [ ! -z "$(git status --porcelain)" ]; then
|
||||
# If we get a fail here then the PR needs attention
|
||||
>&2 echo "Failed: node_modules are not up to date. Run 'npm ci' and 'npm run removeNPMAbsolutePaths' to update"
|
||||
>&2 echo "Failed: node_modules are not up to date. Run 'npm ci && npm run removeNPMAbsolutePaths' on a macOS machine to update. Note it is important this command is run on macOS and not any other operating system as there is one dependency (fsevents) that is needed for macOS and may not be installed if the command is run on a Windows or Linux machine."
|
||||
git status
|
||||
exit 1
|
||||
fi
|
||||
|
||||
+6
-1
@@ -1,6 +1,11 @@
|
||||
# CodeQL Action and CodeQL Runner Changelog
|
||||
|
||||
## [UNRELEASED]
|
||||
## 1.0.2 - 17 Jun 2021
|
||||
|
||||
## 1.0.1 - 07 Jun 2021
|
||||
|
||||
- Pass the `--sarif-group-rules-by-pack` argument to CodeQL CLI invocations that generate SARIF. This means the SARIF rule object for each query will now be found underneath its corresponding query pack in `runs[].tool.extensions`. [#546](https://github.com/github/codeql-action/pull/546)
|
||||
- Output the location of CodeQL databases created in the analyze step. [#543](https://github.com/github/codeql-action/pull/543)
|
||||
|
||||
## 1.0.0 - 31 May 2021
|
||||
|
||||
|
||||
+2
-2
@@ -12,7 +12,7 @@ Please note that this project is released with a [Contributor Code of Conduct][c
|
||||
|
||||
## Development and Testing
|
||||
|
||||
Before you start, ensure that you have a recent version of node installed. You can see which version of node is used by the action in `init/action.yml`.
|
||||
Before you start, ensure that you have a recent version of node (14 or higher) installed, along with a recent version of npm (7 or higher). You can see which version of node is used by the action in `init/action.yml`.
|
||||
|
||||
### Common tasks
|
||||
|
||||
@@ -28,7 +28,7 @@ You may want to run `tsc --watch` from the command line or inside of vscode in o
|
||||
|
||||
Because CodeQL Action users consume the code directly from this repository, and there can be no build step during an GitHub Actions run, this repository contains all compiled artifacts and node modules. There is a PR check that will fail if any of the compiled artifacts are not up to date. Compiled artifacts are stored in the `lib/` directory. For all day-to-day development purposes, this folder can be ignored.
|
||||
|
||||
Only run `npm install` if you are explicitly changing the set of dependencies in `package.json`. The `node_modules` directory should be up to date when you check out, but if for some reason, there is an inconsistency use `npm ci && npm run removeNPMAbsolutePaths` to ensure the directory is in a state consistent with the `package-lock.json`. There is a PR check to ensure the consistency of the `node_modules` directory.
|
||||
Only run `npm install` if you are explicitly changing the set of dependencies in `package.json`. The `node_modules` directory should be up to date when you check out, but if for some reason, there is an inconsistency use `npm ci && npm run removeNPMAbsolutePaths` to ensure the directory is in a state consistent with the `package-lock.json`. Note that due to a macOS-specific dependency, this command should be run on a macOS machine. There is a PR check to ensure the consistency of the `node_modules` directory.
|
||||
|
||||
### Running the action
|
||||
|
||||
|
||||
@@ -13,6 +13,10 @@ inputs:
|
||||
description: Upload the SARIF file
|
||||
required: false
|
||||
default: "true"
|
||||
cleanup-level:
|
||||
description: "Level of cleanup to perform on CodeQL databases at the end of the analyze step. This should either be 'none' to skip cleanup, or be a valid argument for the --mode flag of the CodeQL CLI command 'codeql database cleanup' as documented at https://codeql.github.com/docs/codeql-cli/manual/database-cleanup"
|
||||
required: false
|
||||
default: "brutal"
|
||||
ram:
|
||||
description: Override the amount of memory in MB to be used by CodeQL. By default, almost all the memory of the machine is used.
|
||||
required: false
|
||||
@@ -34,6 +38,9 @@ inputs:
|
||||
default: ${{ github.token }}
|
||||
matrix:
|
||||
default: ${{ toJson(matrix) }}
|
||||
outputs:
|
||||
db-locations:
|
||||
description: A map from language to absolute path for each database created by CodeQL.
|
||||
runs:
|
||||
using: 'node12'
|
||||
main: '../lib/analyze-action.js'
|
||||
|
||||
Generated
+7
@@ -43,6 +43,13 @@ function getOptionalInput(name) {
|
||||
return value.length > 0 ? value : undefined;
|
||||
}
|
||||
exports.getOptionalInput = getOptionalInput;
|
||||
function getTemporaryDirectory() {
|
||||
const value = process.env["CODEQL_ACTION_TEMP"];
|
||||
return value !== undefined && value !== ""
|
||||
? value
|
||||
: util_1.getRequiredEnvParam("RUNNER_TEMP");
|
||||
}
|
||||
exports.getTemporaryDirectory = getTemporaryDirectory;
|
||||
function getToolCacheDirectory() {
|
||||
const value = process.env["CODEQL_ACTION_TOOL_CACHE"];
|
||||
return value !== undefined && value !== ""
|
||||
|
||||
File diff suppressed because one or more lines are too long
Generated
+3
@@ -29,6 +29,7 @@ ava_1.default("emptyPaths", async (t) => {
|
||||
codeQLCmd: "",
|
||||
gitHubVersion: { type: util.GitHubVariant.DOTCOM },
|
||||
dbLocation: path.resolve(tmpDir, "codeql_databases"),
|
||||
packs: {},
|
||||
};
|
||||
analysisPaths.includeAndExcludeAnalysisPaths(config);
|
||||
t.is(process.env["LGTM_INDEX_INCLUDE"], undefined);
|
||||
@@ -49,6 +50,7 @@ ava_1.default("nonEmptyPaths", async (t) => {
|
||||
codeQLCmd: "",
|
||||
gitHubVersion: { type: util.GitHubVariant.DOTCOM },
|
||||
dbLocation: path.resolve(tmpDir, "codeql_databases"),
|
||||
packs: {},
|
||||
};
|
||||
analysisPaths.includeAndExcludeAnalysisPaths(config);
|
||||
t.is(process.env["LGTM_INDEX_INCLUDE"], "path1\npath2");
|
||||
@@ -70,6 +72,7 @@ ava_1.default("exclude temp dir", async (t) => {
|
||||
codeQLCmd: "",
|
||||
gitHubVersion: { type: util.GitHubVariant.DOTCOM },
|
||||
dbLocation: path.resolve(tempDir, "codeql_databases"),
|
||||
packs: {},
|
||||
};
|
||||
analysisPaths.includeAndExcludeAnalysisPaths(config);
|
||||
t.is(process.env["LGTM_INDEX_INCLUDE"], undefined);
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"analysis-paths.test.js","sourceRoot":"","sources":["../src/analysis-paths.test.ts"],"names":[],"mappings":"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"}
|
||||
{"version":3,"file":"analysis-paths.test.js","sourceRoot":"","sources":["../src/analysis-paths.test.ts"],"names":[],"mappings":"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"}
|
||||
Generated
+9
-1
@@ -40,7 +40,7 @@ async function run() {
|
||||
return;
|
||||
}
|
||||
const logger = logging_1.getActionsLogger();
|
||||
config = await config_utils_1.getConfig(util.getTemporaryDirectory(), logger);
|
||||
config = await config_utils_1.getConfig(actionsUtil.getTemporaryDirectory(), logger);
|
||||
if (config === undefined) {
|
||||
throw new Error("Config file could not be found at expected location. Has the 'init' action been called?");
|
||||
}
|
||||
@@ -50,6 +50,14 @@ async function run() {
|
||||
};
|
||||
const outputDir = actionsUtil.getRequiredInput("output");
|
||||
const queriesStats = await analyze_1.runAnalyze(outputDir, util.getMemoryFlag(actionsUtil.getOptionalInput("ram")), util.getAddSnippetsFlag(actionsUtil.getRequiredInput("add-snippets")), util.getThreadsFlag(actionsUtil.getOptionalInput("threads"), logger), actionsUtil.getOptionalInput("category"), config, logger);
|
||||
if (actionsUtil.getOptionalInput("cleanup-level") !== "none") {
|
||||
await analyze_1.runCleanup(config, actionsUtil.getOptionalInput("cleanup-level") || "brutal", logger);
|
||||
}
|
||||
const dbLocations = {};
|
||||
for (const language of config.languages) {
|
||||
dbLocations[language] = util.getCodeQLDatabasePath(config, language);
|
||||
}
|
||||
core.setOutput("db-locations", dbLocations);
|
||||
if (actionsUtil.getRequiredInput("upload") === "true") {
|
||||
const uploadStats = await upload_lib.uploadFromActions(outputDir, config.gitHubVersion, apiDetails, logger);
|
||||
stats = { ...queriesStats, ...uploadStats };
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"analyze-action.js","sourceRoot":"","sources":["../src/analyze-action.ts"],"names":[],"mappings":";;;;;;;;;AAAA,uCAAyB;AACzB,2CAA6B;AAE7B,oDAAsC;AAEtC,4DAA8C;AAC9C,uCAImB;AACnB,iDAAmD;AACnD,uCAA6C;AAC7C,yDAA2C;AAC3C,6CAA+B;AAE/B,8CAA8C;AAC9C,MAAM,GAAG,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAUvC,KAAK,UAAU,gBAAgB,CAC7B,SAAe,EACf,KAAuC,EACvC,KAAa;;IAEb,MAAM,MAAM,GACV,OAAA,KAAK,0CAAE,wBAAwB,MAAK,SAAS,IAAI,KAAK,KAAK,SAAS;QAClE,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,SAAS,CAAC;IAChB,MAAM,gBAAgB,GAAG,MAAM,WAAW,CAAC,sBAAsB,CAC/D,QAAQ,EACR,MAAM,EACN,SAAS,QACT,KAAK,0CAAE,OAAO,QACd,KAAK,0CAAE,KAAK,CACb,CAAC;IACF,MAAM,YAAY,GAAuB;QACvC,GAAG,gBAAgB;QACnB,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC;KACjB,CAAC;IACF,MAAM,WAAW,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAC;AACnD,CAAC;AAED,KAAK,UAAU,GAAG;IAChB,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC;IAC7B,IAAI,KAAK,GAAqC,SAAS,CAAC;IACxD,IAAI,MAAM,GAAuB,SAAS,CAAC;IAC3C,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;IAE3D,IAAI;QACF,IACE,CAAC,CAAC,MAAM,WAAW,CAAC,gBAAgB,CAClC,MAAM,WAAW,CAAC,sBAAsB,CACtC,QAAQ,EACR,UAAU,EACV,SAAS,CACV,CACF,CAAC,EACF;YACA,OAAO;SACR;QACD,MAAM,MAAM,GAAG,0BAAgB,EAAE,CAAC;QAClC,MAAM,GAAG,MAAM,wBAAS,CAAC,IAAI,CAAC,qBAAqB,EAAE,EAAE,MAAM,CAAC,CAAC;QAC/D,IAAI,MAAM,KAAK,SAAS,EAAE;YACxB,MAAM,IAAI,KAAK,CACb,yFAAyF,CAC1F,CAAC;SACH;QAED,MAAM,UAAU,GAAG;YACjB,IAAI,EAAE,WAAW,CAAC,gBAAgB,CAAC,OAAO,CAAC;YAC3C,GAAG,EAAE,IAAI,CAAC,mBAAmB,CAAC,mBAAmB,CAAC;SACnD,CAAC;QACF,MAAM,SAAS,GAAG,WAAW,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QACzD,MAAM,YAAY,GAAG,MAAM,oBAAU,CACnC,SAAS,EACT,IAAI,CAAC,aAAa,CAAC,WAAW,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,EACvD,IAAI,CAAC,kBAAkB,CAAC,WAAW,CAAC,gBAAgB,CAAC,cAAc,CAAC,CAAC,EACrE,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC,gBAAgB,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,EACpE,WAAW,CAAC,gBAAgB,CAAC,UAAU,CAAC,EACxC,MAAM,EACN,MAAM,CACP,CAAC;QAEF,IAAI,WAAW,CAAC,gBAAgB,CAAC,QAAQ,CAAC,KAAK,MAAM,EAAE;YACrD,MAAM,WAAW,GAAG,MAAM,UAAU,CAAC,iBAAiB,CACpD,SAAS,EACT,MAAM,CAAC,aAAa,EACpB,UAAU,EACV,MAAM,CACP,CAAC;YACF,KAAK,GAAG,EAAE,GAAG,YAAY,EAAE,GAAG,WAAW,EAAE,CAAC;SAC7C;aAAM;YACL,MAAM,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;YACrC,KAAK,GAAG,EAAE,GAAG,YAAY,EAAE,CAAC;SAC7B;KACF;IAAC,OAAO,KAAK,EAAE;QACd,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC9B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAEnB,IAAI,KAAK,YAAY,6BAAmB,EAAE;YACxC,KAAK,GAAG,EAAE,GAAG,KAAK,CAAC,mBAAmB,EAAE,CAAC;SAC1C;QAED,MAAM,gBAAgB,CAAC,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;QAChD,OAAO;KACR;YAAS;QACR,IAAI,IAAI,CAAC,OAAO,EAAE,IAAI,MAAM,KAAK,SAAS,EAAE;YAC1C,IAAI,CAAC,IAAI,CAAC,iDAAiD,CAAC,CAAC;YAC7D,KAAK,MAAM,QAAQ,IAAI,MAAM,CAAC,SAAS,EAAE;gBACvC,MAAM,iBAAiB,GAAG,IAAI,CAAC,qBAAqB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;gBACvE,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,KAAK,CAAC,CAAC;gBAE1D,MAAM,YAAY,GAAG,CAAC,GAAW,EAAE,EAAE;oBACnC,MAAM,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;oBAC7D,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE;wBAC3B,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE;4BAClB,IAAI,CAAC,UAAU,CACb,uBAAuB,QAAQ,MAAM,KAAK,CAAC,IAAI,EAAE,CAClD,CAAC;4BACF,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAC/C,CAAC;4BACF,IAAI,CAAC,QAAQ,EAAE,CAAC;yBACjB;6BAAM,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE;4BAC9B,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;yBAC7C;qBACF;gBACH,CAAC,CAAC;gBACF,YAAY,CAAC,aAAa,CAAC,CAAC;aAC7B;SACF;KACF;IAED,MAAM,gBAAgB,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;AAC3C,CAAC;AAED,KAAK,UAAU,UAAU;IACvB,IAAI;QACF,MAAM,GAAG,EAAE,CAAC;KACb;IAAC,OAAO,KAAK,EAAE;QACd,IAAI,CAAC,SAAS,CAAC,0BAA0B,KAAK,EAAE,CAAC,CAAC;QAClD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;KACpB;AACH,CAAC;AAED,KAAK,UAAU,EAAE,CAAC"}
|
||||
{"version":3,"file":"analyze-action.js","sourceRoot":"","sources":["../src/analyze-action.ts"],"names":[],"mappings":"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"}
|
||||
Generated
+74
-47
@@ -15,7 +15,6 @@ const codeql_1 = require("./codeql");
|
||||
const count_loc_1 = require("./count-loc");
|
||||
const languages_1 = require("./languages");
|
||||
const sharedEnv = __importStar(require("./shared-environment"));
|
||||
const upload_lib_1 = require("./upload-lib");
|
||||
const util = __importStar(require("./util"));
|
||||
class CodeQLAnalysisError extends Error {
|
||||
constructor(queriesStatusReport, message) {
|
||||
@@ -77,6 +76,7 @@ async function finalizeDatabaseCreation(config, threadsFlag, logger) {
|
||||
}
|
||||
// Runs queries and creates sarif files in the given folder
|
||||
async function runQueries(sarifFolder, memoryFlag, addSnippetsFlag, threadsFlag, automationDetailsId, config, logger) {
|
||||
var _a, _b;
|
||||
const statusReport = {};
|
||||
// count the number of lines in the background
|
||||
const locPromise = count_loc_1.countLoc(path.resolve(),
|
||||
@@ -85,96 +85,123 @@ async function runQueries(sarifFolder, memoryFlag, addSnippetsFlag, threadsFlag,
|
||||
// that here.
|
||||
config.paths, config.pathsIgnore, config.languages, logger);
|
||||
for (const language of config.languages) {
|
||||
logger.startGroup(`Analyzing ${language}`);
|
||||
const queries = config.queries[language];
|
||||
if (queries === undefined ||
|
||||
(queries.builtin.length === 0 && queries.custom.length === 0)) {
|
||||
const packsWithVersion = config.packs[language] || [];
|
||||
const hasBuiltinQueries = ((_a = queries) === null || _a === void 0 ? void 0 : _a.builtin.length) > 0;
|
||||
const hasCustomQueries = ((_b = queries) === null || _b === void 0 ? void 0 : _b.custom.length) > 0;
|
||||
const hasPackWithCustomQueries = packsWithVersion.length > 0;
|
||||
if (!hasBuiltinQueries && !hasCustomQueries && !hasPackWithCustomQueries) {
|
||||
throw new Error(`Unable to analyse ${language} as no queries were selected for this language`);
|
||||
}
|
||||
try {
|
||||
let analysisSummaryBuiltIn = "";
|
||||
const customAnalysisSummaries = [];
|
||||
if (hasPackWithCustomQueries) {
|
||||
logger.info("*************");
|
||||
logger.info("Performing analysis with custom QL Packs. QL Packs are an experimental feature.");
|
||||
logger.info("And should not be used in production yet.");
|
||||
logger.info("*************");
|
||||
logger.startGroup(`Downloading custom packs for ${language}`);
|
||||
const codeql = codeql_1.getCodeQL(config.codeQLCmd);
|
||||
const results = await codeql.packDownload(packsWithVersion);
|
||||
logger.info(`Downloaded packs: ${results.packs
|
||||
.map((r) => `${r.name}@${r.version || "latest"}`)
|
||||
.join(", ")}`);
|
||||
logger.endGroup();
|
||||
}
|
||||
logger.startGroup(`Running queries for ${language}`);
|
||||
const querySuitePaths = [];
|
||||
if (queries["builtin"].length > 0) {
|
||||
const startTimeBuiltIn = new Date().getTime();
|
||||
const { sarifFile, stdout } = await runQueryGroup(language, "builtin", queries["builtin"], sarifFolder, undefined);
|
||||
analysisSummaryBuiltIn = stdout;
|
||||
await injectLinesOfCode(sarifFile, language, locPromise);
|
||||
querySuitePaths.push(await runQueryGroup(language, "builtin", createQuerySuiteContents(queries["builtin"]), undefined));
|
||||
statusReport[`analyze_builtin_queries_${language}_duration_ms`] =
|
||||
new Date().getTime() - startTimeBuiltIn;
|
||||
}
|
||||
const startTimeCustom = new Date().getTime();
|
||||
const temporarySarifDir = config.tempDir;
|
||||
const temporarySarifFiles = [];
|
||||
let ranCustom = false;
|
||||
for (let i = 0; i < queries["custom"].length; ++i) {
|
||||
if (queries["custom"][i].queries.length > 0) {
|
||||
const { sarifFile, stdout } = await runQueryGroup(language, `custom-${i}`, queries["custom"][i].queries, temporarySarifDir, queries["custom"][i].searchPath);
|
||||
customAnalysisSummaries.push(stdout);
|
||||
temporarySarifFiles.push(sarifFile);
|
||||
querySuitePaths.push(await runQueryGroup(language, `custom-${i}`, createQuerySuiteContents(queries["custom"][i].queries), queries["custom"][i].searchPath));
|
||||
ranCustom = true;
|
||||
}
|
||||
}
|
||||
if (temporarySarifFiles.length > 0) {
|
||||
const sarifFile = path.join(sarifFolder, `${language}-custom.sarif`);
|
||||
fs.writeFileSync(sarifFile, upload_lib_1.combineSarifFiles(temporarySarifFiles));
|
||||
await injectLinesOfCode(sarifFile, language, locPromise);
|
||||
if (packsWithVersion.length > 0) {
|
||||
querySuitePaths.push(await runQueryGroup(language, "packs", createPackSuiteContents(packsWithVersion), undefined));
|
||||
ranCustom = true;
|
||||
}
|
||||
if (ranCustom) {
|
||||
statusReport[`analyze_custom_queries_${language}_duration_ms`] =
|
||||
new Date().getTime() - startTimeCustom;
|
||||
}
|
||||
logger.endGroup();
|
||||
// Print the LoC baseline and the summary results from database analyze for the standard
|
||||
// query suite and (if appropriate) each custom query suite.
|
||||
logger.startGroup(`Analysis summary for ${language}`);
|
||||
printLinesOfCodeSummary(logger, language, await locPromise);
|
||||
logger.info(analysisSummaryBuiltIn);
|
||||
for (const [i, customSummary] of customAnalysisSummaries.entries()) {
|
||||
if (customSummary.trim() === "") {
|
||||
continue;
|
||||
}
|
||||
const description = customAnalysisSummaries.length === 1
|
||||
? "custom queries"
|
||||
: `custom query suite ${i + 1}/${customAnalysisSummaries.length}`;
|
||||
logger.info(`Analysis summary for ${description}:`);
|
||||
logger.info("");
|
||||
logger.info(customSummary);
|
||||
logger.info("");
|
||||
}
|
||||
logger.startGroup(`Interpreting results for ${language}`);
|
||||
const startTimeInterpretResults = new Date().getTime();
|
||||
const sarifFile = path.join(sarifFolder, `${language}.sarif`);
|
||||
const analysisSummary = await runInterpretResults(language, querySuitePaths, sarifFile);
|
||||
await injectLinesOfCode(sarifFile, language, locPromise);
|
||||
statusReport[`interpret_results_${language}_duration_ms`] =
|
||||
new Date().getTime() - startTimeInterpretResults;
|
||||
logger.endGroup();
|
||||
logger.info(analysisSummary);
|
||||
printLinesOfCodeSummary(logger, language, await locPromise);
|
||||
}
|
||||
catch (e) {
|
||||
logger.info(e);
|
||||
logger.info(e.stack);
|
||||
statusReport.analyze_failure_language = language;
|
||||
throw new CodeQLAnalysisError(statusReport, `Error running analysis for ${language}: ${e}`);
|
||||
}
|
||||
}
|
||||
return statusReport;
|
||||
async function runQueryGroup(language, type, queries, destinationFolder, searchPath) {
|
||||
async function runInterpretResults(language, queries, sarifFile) {
|
||||
const databasePath = util.getCodeQLDatabasePath(config, language);
|
||||
const codeql = codeql_1.getCodeQL(config.codeQLCmd);
|
||||
return await codeql.databaseInterpretResults(databasePath, queries, sarifFile, addSnippetsFlag, threadsFlag, automationDetailsId);
|
||||
}
|
||||
async function runQueryGroup(language, type, querySuiteContents, searchPath) {
|
||||
const databasePath = util.getCodeQLDatabasePath(config, language);
|
||||
// Pass the queries to codeql using a file instead of using the command
|
||||
// line to avoid command line length restrictions, particularly on windows.
|
||||
const querySuitePath = `${databasePath}-queries-${type}.qls`;
|
||||
const querySuiteContents = queries
|
||||
.map((q) => `- query: ${q}`)
|
||||
.join("\n");
|
||||
fs.writeFileSync(querySuitePath, querySuiteContents);
|
||||
logger.debug(`Query suite file for ${language}...\n${querySuiteContents}`);
|
||||
const sarifFile = path.join(destinationFolder, `${language}-${type}.sarif`);
|
||||
logger.debug(`Query suite file for ${language}-${type}...\n${querySuiteContents}`);
|
||||
const codeql = codeql_1.getCodeQL(config.codeQLCmd);
|
||||
const databaseAnalyzeStdout = await codeql.databaseAnalyze(databasePath, sarifFile, searchPath, querySuitePath, memoryFlag, addSnippetsFlag, threadsFlag, automationDetailsId);
|
||||
logger.debug(`SARIF results for database ${language} created at "${sarifFile}"`);
|
||||
return { sarifFile, stdout: databaseAnalyzeStdout };
|
||||
await codeql.databaseRunQueries(databasePath, searchPath, querySuitePath, memoryFlag, threadsFlag);
|
||||
logger.debug(`BQRS results produced for ${language} (queries: ${type})"`);
|
||||
return querySuitePath;
|
||||
}
|
||||
}
|
||||
exports.runQueries = runQueries;
|
||||
function createQuerySuiteContents(queries) {
|
||||
return queries.map((q) => `- query: ${q}`).join("\n");
|
||||
}
|
||||
function createPackSuiteContents(packsWithVersion) {
|
||||
return packsWithVersion.map(packWithVersionToQuerySuiteEntry).join("\n");
|
||||
}
|
||||
function packWithVersionToQuerySuiteEntry(pack) {
|
||||
let text = `- qlpack: ${pack.packName}`;
|
||||
if (pack.version) {
|
||||
text += `\n version: ${pack.version}`;
|
||||
}
|
||||
return text;
|
||||
}
|
||||
async function runAnalyze(outputDir, memoryFlag, addSnippetsFlag, threadsFlag, automationDetailsId, config, logger) {
|
||||
// Delete the tracer config env var to avoid tracing ourselves
|
||||
delete process.env[sharedEnv.ODASA_TRACER_CONFIGURATION];
|
||||
fs.mkdirSync(outputDir, { recursive: true });
|
||||
logger.info("Finalizing database creation");
|
||||
await finalizeDatabaseCreation(config, threadsFlag, logger);
|
||||
logger.info("Analyzing database");
|
||||
const queriesStats = await runQueries(outputDir, memoryFlag, addSnippetsFlag, threadsFlag, automationDetailsId, config, logger);
|
||||
return { ...queriesStats };
|
||||
}
|
||||
exports.runAnalyze = runAnalyze;
|
||||
async function runCleanup(config, cleanupLevel, logger) {
|
||||
logger.startGroup("Cleaning up databases");
|
||||
for (const language of config.languages) {
|
||||
const codeql = codeql_1.getCodeQL(config.codeQLCmd);
|
||||
const databasePath = util.getCodeQLDatabasePath(config, language);
|
||||
await codeql.databaseCleanup(databasePath, cleanupLevel);
|
||||
}
|
||||
logger.endGroup();
|
||||
}
|
||||
exports.runCleanup = runCleanup;
|
||||
async function injectLinesOfCode(sarifFile, language, locPromise) {
|
||||
const lineCounts = await locPromise;
|
||||
const idPrefix = count_loc_1.getIdPrefix(language);
|
||||
@@ -199,7 +226,7 @@ async function injectLinesOfCode(sarifFile, language, locPromise) {
|
||||
}
|
||||
function printLinesOfCodeSummary(logger, language, lineCounts) {
|
||||
if (language in lineCounts) {
|
||||
logger.info(`Counted ${lineCounts[language]} lines of code for ${language} as a baseline.`);
|
||||
logger.info(`Counted a baseline of ${lineCounts[language]} lines of code for ${language}.`);
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=analyze.js.map
|
||||
+1
-1
File diff suppressed because one or more lines are too long
Generated
+87
-9
@@ -13,6 +13,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const fs = __importStar(require("fs"));
|
||||
const path = __importStar(require("path"));
|
||||
const ava_1 = __importDefault(require("ava"));
|
||||
const yaml = __importStar(require("js-yaml"));
|
||||
const semver_1 = require("semver");
|
||||
const sinon_1 = __importDefault(require("sinon"));
|
||||
const analyze_1 = require("./analyze");
|
||||
const codeql_1 = require("./codeql");
|
||||
@@ -39,9 +41,27 @@ ava_1.default("status report fields and search path setting", async (t) => {
|
||||
const memoryFlag = "";
|
||||
const addSnippetsFlag = "";
|
||||
const threadsFlag = "";
|
||||
const packs = {
|
||||
[languages_1.Language.cpp]: [
|
||||
{
|
||||
packName: "a/b",
|
||||
version: semver_1.clean("1.0.0"),
|
||||
},
|
||||
],
|
||||
[languages_1.Language.java]: [
|
||||
{
|
||||
packName: "c/d",
|
||||
version: semver_1.clean("2.0.0"),
|
||||
},
|
||||
],
|
||||
};
|
||||
for (const language of Object.values(languages_1.Language)) {
|
||||
codeql_1.setCodeQL({
|
||||
databaseAnalyze: async (_, sarifFile, searchPath) => {
|
||||
packDownload: async () => ({ packs: [] }),
|
||||
databaseRunQueries: async (_db, searchPath) => {
|
||||
searchPathsUsed.push(searchPath);
|
||||
},
|
||||
databaseInterpretResults: async (_db, _queriesRun, sarifFile) => {
|
||||
fs.writeFileSync(sarifFile, JSON.stringify({
|
||||
runs: [
|
||||
// variant 1 uses ruleId
|
||||
@@ -71,7 +91,6 @@ ava_1.default("status report fields and search path setting", async (t) => {
|
||||
{},
|
||||
],
|
||||
}));
|
||||
searchPathsUsed.push(searchPath);
|
||||
return "";
|
||||
},
|
||||
});
|
||||
@@ -89,6 +108,7 @@ ava_1.default("status report fields and search path setting", async (t) => {
|
||||
type: util.GitHubVariant.DOTCOM,
|
||||
},
|
||||
dbLocation: path.resolve(tmpDir, "codeql_databases"),
|
||||
packs,
|
||||
};
|
||||
fs.mkdirSync(util.getCodeQLDatabasePath(config, language), {
|
||||
recursive: true,
|
||||
@@ -98,8 +118,18 @@ ava_1.default("status report fields and search path setting", async (t) => {
|
||||
custom: [],
|
||||
};
|
||||
const builtinStatusReport = await analyze_1.runQueries(tmpDir, memoryFlag, addSnippetsFlag, threadsFlag, undefined, config, logging_1.getRunnerLogger(true));
|
||||
t.deepEqual(Object.keys(builtinStatusReport).length, 1);
|
||||
t.true(`analyze_builtin_queries_${language}_duration_ms` in builtinStatusReport);
|
||||
const hasPacks = language in packs;
|
||||
const statusReportKeys = Object.keys(builtinStatusReport).sort();
|
||||
if (hasPacks) {
|
||||
t.deepEqual(statusReportKeys.length, 3, statusReportKeys.toString());
|
||||
t.deepEqual(statusReportKeys[0], `analyze_builtin_queries_${language}_duration_ms`);
|
||||
t.deepEqual(statusReportKeys[1], `analyze_custom_queries_${language}_duration_ms`);
|
||||
t.deepEqual(statusReportKeys[2], `interpret_results_${language}_duration_ms`);
|
||||
}
|
||||
else {
|
||||
t.deepEqual(statusReportKeys[0], `analyze_builtin_queries_${language}_duration_ms`);
|
||||
t.deepEqual(statusReportKeys[1], `interpret_results_${language}_duration_ms`);
|
||||
}
|
||||
config.queries[language] = {
|
||||
builtin: [],
|
||||
custom: [
|
||||
@@ -114,17 +144,21 @@ ava_1.default("status report fields and search path setting", async (t) => {
|
||||
],
|
||||
};
|
||||
const customStatusReport = await analyze_1.runQueries(tmpDir, memoryFlag, addSnippetsFlag, threadsFlag, undefined, config, logging_1.getRunnerLogger(true));
|
||||
t.deepEqual(Object.keys(customStatusReport).length, 1);
|
||||
t.deepEqual(Object.keys(customStatusReport).length, 2);
|
||||
t.true(`analyze_custom_queries_${language}_duration_ms` in customStatusReport);
|
||||
t.deepEqual(searchPathsUsed, [undefined, "/1", "/2"]);
|
||||
const expectedSearchPathsUsed = hasPacks
|
||||
? [undefined, undefined, "/1", "/2", undefined]
|
||||
: [undefined, "/1", "/2"];
|
||||
t.deepEqual(searchPathsUsed, expectedSearchPathsUsed);
|
||||
t.true(`interpret_results_${language}_duration_ms` in customStatusReport);
|
||||
}
|
||||
verifyLineCounts(tmpDir);
|
||||
verifyQuerySuites(tmpDir);
|
||||
});
|
||||
function verifyLineCounts(tmpDir) {
|
||||
// eslint-disable-next-line github/array-foreach
|
||||
Object.keys(languages_1.Language).forEach((lang, i) => {
|
||||
verifyLineCountForFile(lang, path.join(tmpDir, `${lang}-builtin.sarif`), i + 1);
|
||||
verifyLineCountForFile(lang, path.join(tmpDir, `${lang}-custom.sarif`), i + 1);
|
||||
verifyLineCountForFile(lang, path.join(tmpDir, `${lang}.sarif`), i + 1);
|
||||
});
|
||||
}
|
||||
function verifyLineCountForFile(lang, filePath, lineCount) {
|
||||
@@ -146,8 +180,52 @@ ava_1.default("status report fields and search path setting", async (t) => {
|
||||
baseline: lineCount,
|
||||
},
|
||||
]);
|
||||
// when the rule doesn't exists, it should not be added
|
||||
// when the rule doesn't exist, it should not be added
|
||||
t.deepEqual(sarif.runs[2].properties.metricResults, []);
|
||||
}
|
||||
function verifyQuerySuites(tmpDir) {
|
||||
const qlsContent = [
|
||||
{
|
||||
query: "foo.ql",
|
||||
},
|
||||
];
|
||||
const qlsContent2 = [
|
||||
{
|
||||
query: "bar.ql",
|
||||
},
|
||||
];
|
||||
const qlsPackContentCpp = [
|
||||
{
|
||||
qlpack: "a/b",
|
||||
version: "1.0.0",
|
||||
},
|
||||
];
|
||||
const qlsPackContentJava = [
|
||||
{
|
||||
qlpack: "c/d",
|
||||
version: "2.0.0",
|
||||
},
|
||||
];
|
||||
for (const lang of Object.values(languages_1.Language)) {
|
||||
t.deepEqual(readContents(`${lang}-queries-builtin.qls`), qlsContent);
|
||||
t.deepEqual(readContents(`${lang}-queries-custom-0.qls`), qlsContent);
|
||||
t.deepEqual(readContents(`${lang}-queries-custom-1.qls`), qlsContent2);
|
||||
const packSuiteName = `${lang}-queries-packs.qls`;
|
||||
if (lang === languages_1.Language.cpp) {
|
||||
t.deepEqual(readContents(packSuiteName), qlsPackContentCpp);
|
||||
}
|
||||
else if (lang === languages_1.Language.java) {
|
||||
t.deepEqual(readContents(packSuiteName), qlsPackContentJava);
|
||||
}
|
||||
else {
|
||||
t.false(fs.existsSync(path.join(tmpDir, "codeql_databases", packSuiteName)));
|
||||
}
|
||||
}
|
||||
function readContents(name) {
|
||||
const x = fs.readFileSync(path.join(tmpDir, "codeql_databases", name), "utf8");
|
||||
console.log(x);
|
||||
return yaml.safeLoad(fs.readFileSync(path.join(tmpDir, "codeql_databases", name), "utf8"));
|
||||
}
|
||||
}
|
||||
});
|
||||
//# sourceMappingURL=analyze.test.js.map
|
||||
File diff suppressed because one or more lines are too long
Generated
+3
-1
@@ -12,6 +12,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const path = __importStar(require("path"));
|
||||
const githubUtils = __importStar(require("@actions/github/lib/utils"));
|
||||
const retry = __importStar(require("@octokit/plugin-retry"));
|
||||
const console_log_level_1 = __importDefault(require("console-log-level"));
|
||||
const actions_util_1 = require("./actions-util");
|
||||
const util_1 = require("./util");
|
||||
@@ -24,7 +25,8 @@ var DisallowedAPIVersionReason;
|
||||
})(DisallowedAPIVersionReason = exports.DisallowedAPIVersionReason || (exports.DisallowedAPIVersionReason = {}));
|
||||
exports.getApiClient = function (apiDetails, { allowExternal = false } = {}) {
|
||||
const auth = (allowExternal && apiDetails.externalRepoAuth) || apiDetails.auth;
|
||||
return new githubUtils.GitHub(githubUtils.getOctokitOptions(auth, {
|
||||
const retryingOctokit = githubUtils.GitHub.plugin(retry.retry);
|
||||
return new retryingOctokit(githubUtils.getOctokitOptions(auth, {
|
||||
baseUrl: getApiUrl(apiDetails.url),
|
||||
userAgent: `CodeQL-${util_1.getMode()}/${pkg.version}`,
|
||||
log: console_log_level_1.default({ level: "debug" }),
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"api-client.js","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA6B;AAE7B,uEAAyD;AACzD,0EAAgD;AAEhD,iDAAkD;AAClD,iCAAsD;AAEtD,8CAA8C;AAC9C,MAAM,GAAG,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAEvC,IAAY,0BAGX;AAHD,WAAY,0BAA0B;IACpC,+FAAc,CAAA;IACd,+FAAc,CAAA;AAChB,CAAC,EAHW,0BAA0B,GAA1B,kCAA0B,KAA1B,kCAA0B,QAGrC;AAeY,QAAA,YAAY,GAAG,UAC1B,UAAoC,EACpC,EAAE,aAAa,GAAG,KAAK,EAAE,GAAG,EAAE;IAE9B,MAAM,IAAI,GACR,CAAC,aAAa,IAAI,UAAU,CAAC,gBAAgB,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC;IACpE,OAAO,IAAI,WAAW,CAAC,MAAM,CAC3B,WAAW,CAAC,iBAAiB,CAAC,IAAI,EAAE;QAClC,OAAO,EAAE,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC;QAClC,SAAS,EAAE,UAAU,cAAO,EAAE,IAAI,GAAG,CAAC,OAAO,EAAE;QAC/C,GAAG,EAAE,2BAAe,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;KACzC,CAAC,CACH,CAAC;AACJ,CAAC,CAAC;AAEF,SAAS,SAAS,CAAC,SAAiB;IAClC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC;IAE/B,uDAAuD;IACvD,0CAA0C;IAC1C,IAAI,GAAG,CAAC,QAAQ,KAAK,YAAY,IAAI,GAAG,CAAC,QAAQ,KAAK,gBAAgB,EAAE;QACtE,OAAO,wBAAwB,CAAC;KACjC;IAED,6BAA6B;IAC7B,GAAG,CAAC,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;IACpD,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC;AACxB,CAAC;AAED,uFAAuF;AACvF,qFAAqF;AACrF,+CAA+C;AAC/C,SAAgB,mBAAmB;IACjC,MAAM,UAAU,GAAG;QACjB,IAAI,EAAE,+BAAgB,CAAC,OAAO,CAAC;QAC/B,GAAG,EAAE,0BAAmB,CAAC,mBAAmB,CAAC;KAC9C,CAAC;IAEF,OAAO,oBAAY,CAAC,UAAU,CAAC,CAAC;AAClC,CAAC;AAPD,kDAOC"}
|
||||
{"version":3,"file":"api-client.js","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA6B;AAE7B,uEAAyD;AACzD,6DAA+C;AAC/C,0EAAgD;AAEhD,iDAAkD;AAClD,iCAAsD;AAEtD,8CAA8C;AAC9C,MAAM,GAAG,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAEvC,IAAY,0BAGX;AAHD,WAAY,0BAA0B;IACpC,+FAAc,CAAA;IACd,+FAAc,CAAA;AAChB,CAAC,EAHW,0BAA0B,GAA1B,kCAA0B,KAA1B,kCAA0B,QAGrC;AAeY,QAAA,YAAY,GAAG,UAC1B,UAAoC,EACpC,EAAE,aAAa,GAAG,KAAK,EAAE,GAAG,EAAE;IAE9B,MAAM,IAAI,GACR,CAAC,aAAa,IAAI,UAAU,CAAC,gBAAgB,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC;IACpE,MAAM,eAAe,GAAG,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC/D,OAAO,IAAI,eAAe,CACxB,WAAW,CAAC,iBAAiB,CAAC,IAAI,EAAE;QAClC,OAAO,EAAE,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC;QAClC,SAAS,EAAE,UAAU,cAAO,EAAE,IAAI,GAAG,CAAC,OAAO,EAAE;QAC/C,GAAG,EAAE,2BAAe,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;KACzC,CAAC,CACH,CAAC;AACJ,CAAC,CAAC;AAEF,SAAS,SAAS,CAAC,SAAiB;IAClC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC;IAE/B,uDAAuD;IACvD,0CAA0C;IAC1C,IAAI,GAAG,CAAC,QAAQ,KAAK,YAAY,IAAI,GAAG,CAAC,QAAQ,KAAK,gBAAgB,EAAE;QACtE,OAAO,wBAAwB,CAAC;KACjC;IAED,6BAA6B;IAC7B,GAAG,CAAC,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;IACpD,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC;AACxB,CAAC;AAED,uFAAuF;AACvF,qFAAqF;AACrF,+CAA+C;AAC/C,SAAgB,mBAAmB;IACjC,MAAM,UAAU,GAAG;QACjB,IAAI,EAAE,+BAAgB,CAAC,OAAO,CAAC;QAC/B,GAAG,EAAE,0BAAmB,CAAC,mBAAmB,CAAC;KAC9C,CAAC;IAEF,OAAO,oBAAY,CAAC,UAAU,CAAC,CAAC;AAClC,CAAC;AAPD,kDAOC"}
|
||||
Generated
+4
-1
@@ -19,9 +19,12 @@ const util_1 = require("./util");
|
||||
// eslint-disable-next-line import/no-commonjs
|
||||
const pkg = require("../package.json");
|
||||
testing_utils_1.setupTests(ava_1.default);
|
||||
let pluginStub;
|
||||
let githubStub;
|
||||
ava_1.default.beforeEach(() => {
|
||||
githubStub = sinon_1.default.stub(githubUtils, "GitHub");
|
||||
pluginStub = sinon_1.default.stub(githubUtils.GitHub, "plugin");
|
||||
githubStub = sinon_1.default.stub();
|
||||
pluginStub.returns(githubStub);
|
||||
util_1.initializeEnvironment(util_1.Mode.actions, pkg.version);
|
||||
});
|
||||
ava_1.default("Get the client API", async (t) => {
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"api-client.test.js","sourceRoot":"","sources":["../src/api-client.test.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,uEAAyD;AACzD,8CAA6C;AAC7C,kDAA0B;AAE1B,6CAA4C;AAC5C,mDAA6C;AAC7C,iCAAqD;AAErD,8CAA8C;AAC9C,MAAM,GAAG,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAEvC,0BAAU,CAAC,aAAI,CAAC,CAAC;AAEjB,IAAI,UAA2B,CAAC;AAEhC,aAAI,CAAC,UAAU,CAAC,GAAG,EAAE;IACnB,UAAU,GAAG,eAAK,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;IAC/C,4BAAqB,CAAC,WAAI,CAAC,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;AACnD,CAAC,CAAC,CAAC;AAEH,aAAI,CAAC,oBAAoB,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;IACrC,MAAM,CACJ,CAAC,EACD;QACE,IAAI,EAAE,KAAK;QACX,gBAAgB,EAAE,KAAK;QACvB,GAAG,EAAE,gBAAgB;KACtB,EACD,SAAS,EACT;QACE,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,uBAAuB;QAChC,SAAS,EAAE,iBAAiB,GAAG,CAAC,OAAO,EAAE;KAC1C,CACF,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,aAAI,CAAC,6BAA6B,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;IAC9C,MAAM,CACJ,CAAC,EACD;QACE,IAAI,EAAE,KAAK;QACX,gBAAgB,EAAE,KAAK;QACvB,GAAG,EAAE,gBAAgB;KACtB,EACD,EAAE,aAAa,EAAE,IAAI,EAAE,EACvB;QACE,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,uBAAuB;QAChC,SAAS,EAAE,iBAAiB,GAAG,CAAC,OAAO,EAAE;KAC1C,CACF,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,aAAI,CAAC,yCAAyC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;IAC1D,MAAM,CACJ,CAAC,EACD;QACE,IAAI,EAAE,KAAK;QACX,GAAG,EAAE,gBAAgB;KACtB,EACD,EAAE,aAAa,EAAE,IAAI,EAAE,EACvB;QACE,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,uBAAuB;QAChC,SAAS,EAAE,iBAAiB,GAAG,CAAC,OAAO,EAAE;KAC1C,CACF,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,aAAI,CAAC,oCAAoC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;IACrD,MAAM,CACJ,CAAC,EACD;QACE,IAAI,EAAE,KAAK;QACX,GAAG,EAAE,qCAAqC;KAC3C,EACD,SAAS,EACT;QACE,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,wBAAwB;QACjC,SAAS,EAAE,iBAAiB,GAAG,CAAC,OAAO,EAAE;KAC1C,CACF,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,SAAS,MAAM,CACb,CAA4B,EAC5B,UAAe,EACf,aAAkB,EAClB,QAAa;IAEb,yBAAY,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;IAExC,MAAM,aAAa,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACzC,iEAAiE;IACjE,OAAO,aAAa,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IAC5B,CAAC,CAAC,SAAS,CAAC,aAAa,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;AACzC,CAAC"}
|
||||
{"version":3,"file":"api-client.test.js","sourceRoot":"","sources":["../src/api-client.test.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,uEAAyD;AACzD,8CAA6C;AAC7C,kDAA0B;AAE1B,6CAA4C;AAC5C,mDAA6C;AAC7C,iCAAqD;AAErD,8CAA8C;AAC9C,MAAM,GAAG,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAEvC,0BAAU,CAAC,aAAI,CAAC,CAAC;AAEjB,IAAI,UAA2B,CAAC;AAChC,IAAI,UAA2B,CAAC;AAEhC,aAAI,CAAC,UAAU,CAAC,GAAG,EAAE;IACnB,UAAU,GAAG,eAAK,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACtD,UAAU,GAAG,eAAK,CAAC,IAAI,EAAE,CAAC;IAC1B,UAAU,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC/B,4BAAqB,CAAC,WAAI,CAAC,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;AACnD,CAAC,CAAC,CAAC;AAEH,aAAI,CAAC,oBAAoB,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;IACrC,MAAM,CACJ,CAAC,EACD;QACE,IAAI,EAAE,KAAK;QACX,gBAAgB,EAAE,KAAK;QACvB,GAAG,EAAE,gBAAgB;KACtB,EACD,SAAS,EACT;QACE,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,uBAAuB;QAChC,SAAS,EAAE,iBAAiB,GAAG,CAAC,OAAO,EAAE;KAC1C,CACF,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,aAAI,CAAC,6BAA6B,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;IAC9C,MAAM,CACJ,CAAC,EACD;QACE,IAAI,EAAE,KAAK;QACX,gBAAgB,EAAE,KAAK;QACvB,GAAG,EAAE,gBAAgB;KACtB,EACD,EAAE,aAAa,EAAE,IAAI,EAAE,EACvB;QACE,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,uBAAuB;QAChC,SAAS,EAAE,iBAAiB,GAAG,CAAC,OAAO,EAAE;KAC1C,CACF,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,aAAI,CAAC,yCAAyC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;IAC1D,MAAM,CACJ,CAAC,EACD;QACE,IAAI,EAAE,KAAK;QACX,GAAG,EAAE,gBAAgB;KACtB,EACD,EAAE,aAAa,EAAE,IAAI,EAAE,EACvB;QACE,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,uBAAuB;QAChC,SAAS,EAAE,iBAAiB,GAAG,CAAC,OAAO,EAAE;KAC1C,CACF,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,aAAI,CAAC,oCAAoC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;IACrD,MAAM,CACJ,CAAC,EACD;QACE,IAAI,EAAE,KAAK;QACX,GAAG,EAAE,qCAAqC;KAC3C,EACD,SAAS,EACT;QACE,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,wBAAwB;QACjC,SAAS,EAAE,iBAAiB,GAAG,CAAC,OAAO,EAAE;KAC1C,CACF,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,SAAS,MAAM,CACb,CAA4B,EAC5B,UAAe,EACf,aAAkB,EAClB,QAAa;IAEb,yBAAY,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;IAExC,MAAM,aAAa,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACzC,iEAAiE;IACjE,OAAO,aAAa,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IAC5B,CAAC,CAAC,SAAS,CAAC,aAAa,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;AACzC,CAAC"}
|
||||
@@ -1 +1 @@
|
||||
{ "maximumVersion": "3.1", "minimumVersion": "2.22" }
|
||||
{ "maximumVersion": "3.2", "minimumVersion": "2.22" }
|
||||
|
||||
Generated
+1
-1
@@ -37,7 +37,7 @@ async function run() {
|
||||
if (!(await actions_util_1.sendStatusReport(await actions_util_1.createStatusReportBase("autobuild", "starting", startedAt)))) {
|
||||
return;
|
||||
}
|
||||
const config = await config_utils.getConfig(util_1.getTemporaryDirectory(), logger);
|
||||
const config = await config_utils.getConfig(actions_util_1.getTemporaryDirectory(), logger);
|
||||
if (config === undefined) {
|
||||
throw new Error("Config file could not be found at expected location. Has the 'init' action been called?");
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"autobuild-action.js","sourceRoot":"","sources":["../src/autobuild-action.ts"],"names":[],"mappings":";;;;;;;;;AAAA,oDAAsC;AAEtC,iDAIwB;AACxB,2CAAuE;AACvE,6DAA+C;AAE/C,uCAA6C;AAC7C,iCAA4E;AAE5E,8CAA8C;AAC9C,MAAM,GAAG,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;AASvC,KAAK,UAAU,yBAAyB,CACtC,SAAe,EACf,YAAsB,EACtB,eAAwB,EACxB,KAAa;;IAEb,4BAAqB,CAAC,WAAI,CAAC,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;IAEjD,MAAM,MAAM,GACV,eAAe,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS;QAClD,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,SAAS,CAAC;IAChB,MAAM,gBAAgB,GAAG,MAAM,qCAAsB,CACnD,WAAW,EACX,MAAM,EACN,SAAS,QACT,KAAK,0CAAE,OAAO,QACd,KAAK,0CAAE,KAAK,CACb,CAAC;IACF,MAAM,YAAY,GAA0B;QAC1C,GAAG,gBAAgB;QACnB,mBAAmB,EAAE,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC;QAC3C,iBAAiB,EAAE,eAAe;KACnC,CAAC;IACF,MAAM,+BAAgB,CAAC,YAAY,CAAC,CAAC;AACvC,CAAC;AAED,KAAK,UAAU,GAAG;IAChB,MAAM,MAAM,GAAG,0BAAgB,EAAE,CAAC;IAClC,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC;IAC7B,IAAI,QAAQ,GAAyB,SAAS,CAAC;IAC/C,IAAI;QACF,IACE,CAAC,CAAC,MAAM,+BAAgB,CACtB,MAAM,qCAAsB,CAAC,WAAW,EAAE,UAAU,EAAE,SAAS,CAAC,CACjE,CAAC,EACF;YACA,OAAO;SACR;QAED,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,SAAS,CACzC,4BAAqB,EAAE,EACvB,MAAM,CACP,CAAC;QACF,IAAI,MAAM,KAAK,SAAS,EAAE;YACxB,MAAM,IAAI,KAAK,CACb,yFAAyF,CAC1F,CAAC;SACH;QACD,QAAQ,GAAG,sCAA0B,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACtD,IAAI,QAAQ,KAAK,SAAS,EAAE;YAC1B,MAAM,wBAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;SAC9C;KACF;IAAC,OAAO,KAAK,EAAE;QACd,IAAI,CAAC,SAAS,CACZ,mIAAmI,KAAK,CAAC,OAAO,EAAE,CACnJ,CAAC;QACF,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACnB,MAAM,yBAAyB,CAC7B,SAAS,EACT,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,EAC1B,QAAQ,EACR,KAAK,CACN,CAAC;QACF,OAAO;KACR;IAED,MAAM,yBAAyB,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AACzE,CAAC;AAED,KAAK,UAAU,UAAU;IACvB,IAAI;QACF,MAAM,GAAG,EAAE,CAAC;KACb;IAAC,OAAO,KAAK,EAAE;QACd,IAAI,CAAC,SAAS,CAAC,4BAA4B,KAAK,EAAE,CAAC,CAAC;QACpD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;KACpB;AACH,CAAC;AAED,KAAK,UAAU,EAAE,CAAC"}
|
||||
{"version":3,"file":"autobuild-action.js","sourceRoot":"","sources":["../src/autobuild-action.ts"],"names":[],"mappings":";;;;;;;;;AAAA,oDAAsC;AAEtC,iDAKwB;AACxB,2CAAuE;AACvE,6DAA+C;AAE/C,uCAA6C;AAC7C,iCAAqD;AAErD,8CAA8C;AAC9C,MAAM,GAAG,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;AASvC,KAAK,UAAU,yBAAyB,CACtC,SAAe,EACf,YAAsB,EACtB,eAAwB,EACxB,KAAa;;IAEb,4BAAqB,CAAC,WAAI,CAAC,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;IAEjD,MAAM,MAAM,GACV,eAAe,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS;QAClD,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,SAAS,CAAC;IAChB,MAAM,gBAAgB,GAAG,MAAM,qCAAsB,CACnD,WAAW,EACX,MAAM,EACN,SAAS,QACT,KAAK,0CAAE,OAAO,QACd,KAAK,0CAAE,KAAK,CACb,CAAC;IACF,MAAM,YAAY,GAA0B;QAC1C,GAAG,gBAAgB;QACnB,mBAAmB,EAAE,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC;QAC3C,iBAAiB,EAAE,eAAe;KACnC,CAAC;IACF,MAAM,+BAAgB,CAAC,YAAY,CAAC,CAAC;AACvC,CAAC;AAED,KAAK,UAAU,GAAG;IAChB,MAAM,MAAM,GAAG,0BAAgB,EAAE,CAAC;IAClC,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC;IAC7B,IAAI,QAAQ,GAAyB,SAAS,CAAC;IAC/C,IAAI;QACF,IACE,CAAC,CAAC,MAAM,+BAAgB,CACtB,MAAM,qCAAsB,CAAC,WAAW,EAAE,UAAU,EAAE,SAAS,CAAC,CACjE,CAAC,EACF;YACA,OAAO;SACR;QAED,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,SAAS,CACzC,oCAAqB,EAAE,EACvB,MAAM,CACP,CAAC;QACF,IAAI,MAAM,KAAK,SAAS,EAAE;YACxB,MAAM,IAAI,KAAK,CACb,yFAAyF,CAC1F,CAAC;SACH;QACD,QAAQ,GAAG,sCAA0B,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACtD,IAAI,QAAQ,KAAK,SAAS,EAAE;YAC1B,MAAM,wBAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;SAC9C;KACF;IAAC,OAAO,KAAK,EAAE;QACd,IAAI,CAAC,SAAS,CACZ,mIAAmI,KAAK,CAAC,OAAO,EAAE,CACnJ,CAAC;QACF,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACnB,MAAM,yBAAyB,CAC7B,SAAS,EACT,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,EAC1B,QAAQ,EACR,KAAK,CACN,CAAC;QACF,OAAO;KACR;IAED,MAAM,yBAAyB,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AACzE,CAAC;AAED,KAAK,UAAU,UAAU;IACvB,IAAI;QACF,MAAM,GAAG,EAAE,CAAC;KACb;IAAC,OAAO,KAAK,EAAE;QACd,IAAI,CAAC,SAAS,CAAC,4BAA4B,KAAK,EAAE,CAAC,CAAC;QACpD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;KACpB;AACH,CAAC;AAED,KAAK,UAAU,EAAE,CAAC"}
|
||||
Generated
+96
-45
@@ -285,7 +285,10 @@ function setCodeQL(partialCodeql) {
|
||||
finalizeDatabase: resolveFunction(partialCodeql, "finalizeDatabase"),
|
||||
resolveLanguages: resolveFunction(partialCodeql, "resolveLanguages"),
|
||||
resolveQueries: resolveFunction(partialCodeql, "resolveQueries"),
|
||||
databaseAnalyze: resolveFunction(partialCodeql, "databaseAnalyze"),
|
||||
packDownload: resolveFunction(partialCodeql, "packDownload"),
|
||||
databaseCleanup: resolveFunction(partialCodeql, "databaseCleanup"),
|
||||
databaseRunQueries: resolveFunction(partialCodeql, "databaseRunQueries"),
|
||||
databaseInterpretResults: resolveFunction(partialCodeql, "databaseInterpretResults"),
|
||||
};
|
||||
return cachedCodeQL;
|
||||
}
|
||||
@@ -310,7 +313,7 @@ function getCodeQLForCmd(cmd) {
|
||||
return cmd;
|
||||
},
|
||||
async printVersion() {
|
||||
await new toolrunner.ToolRunner(cmd, ["version", "--format=json"]).exec();
|
||||
await runTool(cmd, ["version", "--format=json"]);
|
||||
},
|
||||
async getTracerEnv(databasePath) {
|
||||
// Write tracer-env.js to a temp location.
|
||||
@@ -341,7 +344,7 @@ function getCodeQLForCmd(cmd) {
|
||||
// action/runner has been implemented in `codeql database trace-command`
|
||||
// _and_ is present in the latest supported CLI release.)
|
||||
const envFile = path.resolve(databasePath, "working", "env.tmp");
|
||||
await new toolrunner.ToolRunner(cmd, [
|
||||
await runTool(cmd, [
|
||||
"database",
|
||||
"trace-command",
|
||||
databasePath,
|
||||
@@ -349,18 +352,18 @@ function getCodeQLForCmd(cmd) {
|
||||
process.execPath,
|
||||
tracerEnvJs,
|
||||
envFile,
|
||||
]).exec();
|
||||
]);
|
||||
return JSON.parse(fs.readFileSync(envFile, "utf-8"));
|
||||
},
|
||||
async databaseInit(databasePath, language, sourceRoot) {
|
||||
await new toolrunner.ToolRunner(cmd, [
|
||||
await runTool(cmd, [
|
||||
"database",
|
||||
"init",
|
||||
databasePath,
|
||||
`--language=${language}`,
|
||||
`--source-root=${sourceRoot}`,
|
||||
...getExtraOptionsFromEnv(["database", "init"]),
|
||||
]).exec();
|
||||
]);
|
||||
},
|
||||
async runAutobuild(language) {
|
||||
const cmdName = process.platform === "win32" ? "autobuild.cmd" : "autobuild.sh";
|
||||
@@ -376,7 +379,7 @@ function getCodeQLForCmd(cmd) {
|
||||
"-Dhttp.keepAlive=false",
|
||||
"-Dmaven.wagon.http.pool=false",
|
||||
].join(" ");
|
||||
await new toolrunner.ToolRunner(autobuildCmd).exec();
|
||||
await runTool(autobuildCmd);
|
||||
},
|
||||
async extractScannedLanguage(databasePath, language) {
|
||||
// Get extractor location
|
||||
@@ -415,6 +418,7 @@ function getCodeQLForCmd(cmd) {
|
||||
await toolrunner_error_catcher_1.toolrunnerErrorCatcher(cmd, [
|
||||
"database",
|
||||
"finalize",
|
||||
"--finalize-dataset",
|
||||
threadsFlag,
|
||||
...getExtraOptionsFromEnv(["database", "finalize"]),
|
||||
databasePath,
|
||||
@@ -422,14 +426,7 @@ function getCodeQLForCmd(cmd) {
|
||||
},
|
||||
async resolveLanguages() {
|
||||
const codeqlArgs = ["resolve", "languages", "--format=json"];
|
||||
let output = "";
|
||||
await new toolrunner.ToolRunner(cmd, codeqlArgs, {
|
||||
listeners: {
|
||||
stdout: (data) => {
|
||||
output += data.toString();
|
||||
},
|
||||
},
|
||||
}).exec();
|
||||
const output = await runTool(cmd, codeqlArgs);
|
||||
try {
|
||||
return JSON.parse(output);
|
||||
}
|
||||
@@ -448,14 +445,7 @@ function getCodeQLForCmd(cmd) {
|
||||
if (extraSearchPath !== undefined) {
|
||||
codeqlArgs.push("--additional-packs", extraSearchPath);
|
||||
}
|
||||
let output = "";
|
||||
await new toolrunner.ToolRunner(cmd, codeqlArgs, {
|
||||
listeners: {
|
||||
stdout: (data) => {
|
||||
output += data.toString();
|
||||
},
|
||||
},
|
||||
}).exec();
|
||||
const output = await runTool(cmd, codeqlArgs);
|
||||
try {
|
||||
return JSON.parse(output);
|
||||
}
|
||||
@@ -463,43 +453,93 @@ function getCodeQLForCmd(cmd) {
|
||||
throw new Error(`Unexpected output from codeql resolve queries: ${e}`);
|
||||
}
|
||||
},
|
||||
async databaseAnalyze(databasePath, sarifFile, extraSearchPath, querySuite, memoryFlag, addSnippetsFlag, threadsFlag, automationDetailsId) {
|
||||
const args = [
|
||||
async databaseRunQueries(databasePath, extraSearchPath, querySuitePath, memoryFlag, threadsFlag) {
|
||||
const codeqlArgs = [
|
||||
"database",
|
||||
"analyze",
|
||||
"run-queries",
|
||||
memoryFlag,
|
||||
threadsFlag,
|
||||
databasePath,
|
||||
"--min-disk-free=1024",
|
||||
"--format=sarif-latest",
|
||||
"--sarif-multicause-markdown",
|
||||
`--output=${sarifFile}`,
|
||||
addSnippetsFlag,
|
||||
// Enable progress verbosity so we log each query as it's interpreted. This aids debugging
|
||||
// when interpretation takes a while for one of the queries being analyzed.
|
||||
"-v",
|
||||
...getExtraOptionsFromEnv(["database", "analyze"]),
|
||||
...getExtraOptionsFromEnv(["database", "run-queries"]),
|
||||
];
|
||||
if (extraSearchPath !== undefined) {
|
||||
args.push("--additional-packs", extraSearchPath);
|
||||
codeqlArgs.push("--additional-packs", extraSearchPath);
|
||||
}
|
||||
codeqlArgs.push(querySuitePath);
|
||||
await runTool(cmd, codeqlArgs);
|
||||
},
|
||||
async databaseInterpretResults(databasePath, querySuitePaths, sarifFile, addSnippetsFlag, threadsFlag, automationDetailsId) {
|
||||
const codeqlArgs = [
|
||||
"database",
|
||||
"interpret-results",
|
||||
threadsFlag,
|
||||
"--format=sarif-latest",
|
||||
"--print-metrics-summary",
|
||||
"--sarif-group-rules-by-pack",
|
||||
"-v",
|
||||
`--output=${sarifFile}`,
|
||||
addSnippetsFlag,
|
||||
...getExtraOptionsFromEnv(["database", "interpret-results"]),
|
||||
];
|
||||
if (automationDetailsId !== undefined) {
|
||||
args.push("--sarif-category", automationDetailsId);
|
||||
codeqlArgs.push("--sarif-category", automationDetailsId);
|
||||
}
|
||||
args.push(querySuite);
|
||||
codeqlArgs.push(databasePath, ...querySuitePaths);
|
||||
// capture stdout, which contains analysis summaries
|
||||
let output = "";
|
||||
await new toolrunner.ToolRunner(cmd, args, {
|
||||
listeners: {
|
||||
stdout: (data) => {
|
||||
output += data.toString("utf8");
|
||||
},
|
||||
},
|
||||
}).exec();
|
||||
return output;
|
||||
return await runTool(cmd, codeqlArgs);
|
||||
},
|
||||
/**
|
||||
* Download specified packs into the package cache. If the specified
|
||||
* package and version already exists (e.g., from a previous analysis run),
|
||||
* then it is not downloaded again (unless the extra option `--force` is
|
||||
* specified).
|
||||
*
|
||||
* If no version is specified, then the latest version is
|
||||
* downloaded. The check to determine what the latest version is is done
|
||||
* each time this package is requested.
|
||||
*/
|
||||
async packDownload(packs) {
|
||||
const codeqlArgs = [
|
||||
"pack",
|
||||
"download",
|
||||
"--format=json",
|
||||
...getExtraOptionsFromEnv(["pack", "download"]),
|
||||
...packs.map(packWithVersionToString),
|
||||
];
|
||||
const output = await runTool(cmd, codeqlArgs);
|
||||
try {
|
||||
const parsedOutput = JSON.parse(output);
|
||||
if (Array.isArray(parsedOutput.packs) &&
|
||||
// TODO PackDownloadOutput will not include the version if it is not specified
|
||||
// in the input. The version is always the latest version available.
|
||||
// It should be added to the output, but this requires a CLI change
|
||||
parsedOutput.packs.every((p) => p.name /* && p.version */)) {
|
||||
return parsedOutput;
|
||||
}
|
||||
else {
|
||||
throw new Error("Unexpected output from pack download");
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
throw new Error(`Attempted to download specified packs but got an error:\n${output}\n${e}`);
|
||||
}
|
||||
},
|
||||
async databaseCleanup(databasePath, cleanupLevel) {
|
||||
const codeqlArgs = [
|
||||
"database",
|
||||
"cleanup",
|
||||
databasePath,
|
||||
`--mode=${cleanupLevel}`,
|
||||
];
|
||||
await runTool(cmd, codeqlArgs);
|
||||
},
|
||||
};
|
||||
}
|
||||
function packWithVersionToString(pack) {
|
||||
return pack.version ? `${pack.packName}@${pack.version}` : pack.packName;
|
||||
}
|
||||
/**
|
||||
* Gets the options for `path` of `options` as an array of extra option strings.
|
||||
*/
|
||||
@@ -546,4 +586,15 @@ function getExtraOptions(options, paths, pathInfo) {
|
||||
return all.concat(specific);
|
||||
}
|
||||
exports.getExtraOptions = getExtraOptions;
|
||||
async function runTool(cmd, args = []) {
|
||||
let output = "";
|
||||
await new toolrunner.ToolRunner(cmd, args, {
|
||||
listeners: {
|
||||
stdout: (data) => {
|
||||
output += data.toString();
|
||||
},
|
||||
},
|
||||
}).exec();
|
||||
return output;
|
||||
}
|
||||
//# sourceMappingURL=codeql.js.map
|
||||
+1
-1
File diff suppressed because one or more lines are too long
Generated
+94
-7
@@ -10,6 +10,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const fs = __importStar(require("fs"));
|
||||
const path = __importStar(require("path"));
|
||||
const yaml = __importStar(require("js-yaml"));
|
||||
const semver = __importStar(require("semver"));
|
||||
const api = __importStar(require("./api-client"));
|
||||
const externalQueries = __importStar(require("./external-queries"));
|
||||
const languages_1 = require("./languages");
|
||||
@@ -20,6 +21,7 @@ const QUERIES_PROPERTY = "queries";
|
||||
const QUERIES_USES_PROPERTY = "uses";
|
||||
const PATHS_IGNORE_PROPERTY = "paths-ignore";
|
||||
const PATHS_PROPERTY = "paths";
|
||||
const PACKS_PROPERTY = "packs";
|
||||
/**
|
||||
* A list of queries from https://github.com/github/codeql that
|
||||
* we don't want to run. Disabling them here is a quicker alternative to
|
||||
@@ -254,6 +256,22 @@ function getPathsInvalid(configFile) {
|
||||
return getConfigFilePropertyError(configFile, PATHS_PROPERTY, "must be an array of non-empty strings");
|
||||
}
|
||||
exports.getPathsInvalid = getPathsInvalid;
|
||||
function getPacksRequireLanguage(lang, configFile) {
|
||||
return getConfigFilePropertyError(configFile, PACKS_PROPERTY, `has "${lang}", but it is not one of the languages to analyze`);
|
||||
}
|
||||
exports.getPacksRequireLanguage = getPacksRequireLanguage;
|
||||
function getPacksInvalidSplit(configFile) {
|
||||
return getConfigFilePropertyError(configFile, PACKS_PROPERTY, "must split packages by language");
|
||||
}
|
||||
exports.getPacksInvalidSplit = getPacksInvalidSplit;
|
||||
function getPacksInvalid(configFile) {
|
||||
return getConfigFilePropertyError(configFile, PACKS_PROPERTY, "must be an array of non-empty strings");
|
||||
}
|
||||
exports.getPacksInvalid = getPacksInvalid;
|
||||
function getPacksStrInvalid(packStr, configFile) {
|
||||
return getConfigFilePropertyError(configFile, PACKS_PROPERTY, `"${packStr}" is not a valid pack`);
|
||||
}
|
||||
exports.getPacksStrInvalid = getPacksStrInvalid;
|
||||
function getLocalPathOutsideOfRepository(configFile, localPath) {
|
||||
return getConfigFilePropertyError(configFile, `${QUERIES_PROPERTY}.${QUERIES_USES_PROPERTY}`, `is invalid as the local path "${localPath}" is outside of the repository`);
|
||||
}
|
||||
@@ -409,6 +427,7 @@ async function getDefaultConfig(languagesInput, queriesInput, dbLocation, reposi
|
||||
queries,
|
||||
pathsIgnore: [],
|
||||
paths: [],
|
||||
packs: {},
|
||||
originalUserInput: {},
|
||||
tempDir,
|
||||
toolCacheDir,
|
||||
@@ -422,6 +441,7 @@ exports.getDefaultConfig = getDefaultConfig;
|
||||
* Load the config from the given file.
|
||||
*/
|
||||
async function loadConfig(languagesInput, queriesInput, configFile, dbLocation, repository, tempDir, toolCacheDir, codeQL, checkoutPath, gitHubVersion, apiDetails, logger) {
|
||||
var _a;
|
||||
let parsedYAML;
|
||||
if (isLocal(configFile)) {
|
||||
// Treat the config file as relative to the workspace
|
||||
@@ -470,10 +490,11 @@ async function loadConfig(languagesInput, queriesInput, configFile, dbLocation,
|
||||
}
|
||||
if (shouldAddConfigFileQueries(queriesInput) &&
|
||||
QUERIES_PROPERTY in parsedYAML) {
|
||||
if (!(parsedYAML[QUERIES_PROPERTY] instanceof Array)) {
|
||||
const queriesArr = parsedYAML[QUERIES_PROPERTY];
|
||||
if (!Array.isArray(queriesArr)) {
|
||||
throw new Error(getQueriesInvalid(configFile));
|
||||
}
|
||||
for (const query of parsedYAML[QUERIES_PROPERTY]) {
|
||||
for (const query of queriesArr) {
|
||||
if (!(QUERIES_USES_PROPERTY in query) ||
|
||||
typeof query[QUERIES_USES_PROPERTY] !== "string") {
|
||||
throw new Error(getQueryUsesInvalid(configFile));
|
||||
@@ -482,7 +503,7 @@ async function loadConfig(languagesInput, queriesInput, configFile, dbLocation,
|
||||
}
|
||||
}
|
||||
if (PATHS_IGNORE_PROPERTY in parsedYAML) {
|
||||
if (!(parsedYAML[PATHS_IGNORE_PROPERTY] instanceof Array)) {
|
||||
if (!Array.isArray(parsedYAML[PATHS_IGNORE_PROPERTY])) {
|
||||
throw new Error(getPathsIgnoreInvalid(configFile));
|
||||
}
|
||||
for (const ignorePath of parsedYAML[PATHS_IGNORE_PROPERTY]) {
|
||||
@@ -493,7 +514,7 @@ async function loadConfig(languagesInput, queriesInput, configFile, dbLocation,
|
||||
}
|
||||
}
|
||||
if (PATHS_PROPERTY in parsedYAML) {
|
||||
if (!(parsedYAML[PATHS_PROPERTY] instanceof Array)) {
|
||||
if (!Array.isArray(parsedYAML[PATHS_PROPERTY])) {
|
||||
throw new Error(getPathsInvalid(configFile));
|
||||
}
|
||||
for (const includePath of parsedYAML[PATHS_PROPERTY]) {
|
||||
@@ -503,11 +524,13 @@ async function loadConfig(languagesInput, queriesInput, configFile, dbLocation,
|
||||
paths.push(validateAndSanitisePath(includePath, PATHS_PROPERTY, configFile, logger));
|
||||
}
|
||||
}
|
||||
const packs = parsePacks((_a = parsedYAML[PACKS_PROPERTY], (_a !== null && _a !== void 0 ? _a : {})), languages, configFile);
|
||||
return {
|
||||
languages,
|
||||
queries,
|
||||
pathsIgnore,
|
||||
paths,
|
||||
packs,
|
||||
originalUserInput: parsedYAML,
|
||||
tempDir,
|
||||
toolCacheDir,
|
||||
@@ -516,6 +539,68 @@ async function loadConfig(languagesInput, queriesInput, configFile, dbLocation,
|
||||
dbLocation: dbLocationOrDefault(dbLocation, tempDir),
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Pack names must be in the form of `scope/name`, with only alpha-numeric characters,
|
||||
* and `-` allowed as long as not the first or last char.
|
||||
**/
|
||||
const PACK_IDENTIFIER_PATTERN = (function () {
|
||||
const alphaNumeric = "[a-z0-9]";
|
||||
const alphaNumericDash = "[a-z0-9-]";
|
||||
const component = `${alphaNumeric}(${alphaNumericDash}*${alphaNumeric})?`;
|
||||
return new RegExp(`^${component}/${component}$`);
|
||||
})();
|
||||
// Exported for testing
|
||||
function parsePacks(packsByLanguage, languages, configFile) {
|
||||
const packs = {};
|
||||
if (Array.isArray(packsByLanguage)) {
|
||||
if (languages.length === 1) {
|
||||
// single language analysis, so language is implicit
|
||||
packsByLanguage = {
|
||||
[languages[0]]: packsByLanguage,
|
||||
};
|
||||
}
|
||||
else {
|
||||
// this is an error since multi-language analysis requires
|
||||
// packs split by language
|
||||
throw new Error(getPacksInvalidSplit(configFile));
|
||||
}
|
||||
}
|
||||
for (const [lang, packsArr] of Object.entries(packsByLanguage)) {
|
||||
if (!Array.isArray(packsArr)) {
|
||||
throw new Error(getPacksInvalid(configFile));
|
||||
}
|
||||
if (!languages.includes(lang)) {
|
||||
throw new Error(getPacksRequireLanguage(lang, configFile));
|
||||
}
|
||||
packs[lang] = [];
|
||||
for (const packStr of packsArr) {
|
||||
packs[lang].push(toPackWithVersion(packStr, configFile));
|
||||
}
|
||||
}
|
||||
return packs;
|
||||
}
|
||||
exports.parsePacks = parsePacks;
|
||||
function toPackWithVersion(packStr, configFile) {
|
||||
if (typeof packStr !== "string") {
|
||||
throw new Error(getPacksStrInvalid(packStr, configFile));
|
||||
}
|
||||
const nameWithVersion = packStr.split("@");
|
||||
let version;
|
||||
if (nameWithVersion.length > 2 ||
|
||||
!PACK_IDENTIFIER_PATTERN.test(nameWithVersion[0])) {
|
||||
throw new Error(getPacksStrInvalid(packStr, configFile));
|
||||
}
|
||||
else if (nameWithVersion.length === 2) {
|
||||
version = semver.clean(nameWithVersion[1]) || undefined;
|
||||
if (!version) {
|
||||
throw new Error(getPacksStrInvalid(packStr, configFile));
|
||||
}
|
||||
}
|
||||
return {
|
||||
packName: nameWithVersion[0],
|
||||
version,
|
||||
};
|
||||
}
|
||||
function dbLocationOrDefault(dbLocation, tempDir) {
|
||||
return dbLocation || path.resolve(tempDir, "codeql_databases");
|
||||
}
|
||||
@@ -526,6 +611,7 @@ function dbLocationOrDefault(dbLocation, tempDir) {
|
||||
* a default config. The parsed config is then stored to a known location.
|
||||
*/
|
||||
async function initConfig(languagesInput, queriesInput, configFile, dbLocation, repository, tempDir, toolCacheDir, codeQL, checkoutPath, gitHubVersion, apiDetails, logger) {
|
||||
var _a, _b, _c;
|
||||
let config;
|
||||
// If no config file was provided create an empty one
|
||||
if (!configFile) {
|
||||
@@ -538,9 +624,10 @@ async function initConfig(languagesInput, queriesInput, configFile, dbLocation,
|
||||
// The list of queries should not be empty for any language. If it is then
|
||||
// it is a user configuration error.
|
||||
for (const language of config.languages) {
|
||||
if (config.queries[language] === undefined ||
|
||||
(config.queries[language].builtin.length === 0 &&
|
||||
config.queries[language].custom.length === 0)) {
|
||||
const hasBuiltinQueries = ((_a = config.queries[language]) === null || _a === void 0 ? void 0 : _a.builtin.length) > 0;
|
||||
const hasCustomQueries = ((_b = config.queries[language]) === null || _b === void 0 ? void 0 : _b.custom.length) > 0;
|
||||
const hasPacks = (((_c = config.packs[language]) === null || _c === void 0 ? void 0 : _c.length) || 0) > 0;
|
||||
if (!hasPacks && !hasBuiltinQueries && !hasCustomQueries) {
|
||||
throw new Error(`Did not detect any queries to run for ${language}. ` +
|
||||
"Please make sure that the default queries are enabled, or you are specifying queries to run.");
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
Generated
+147
@@ -14,6 +14,7 @@ const fs = __importStar(require("fs"));
|
||||
const path = __importStar(require("path"));
|
||||
const github = __importStar(require("@actions/github"));
|
||||
const ava_1 = __importDefault(require("ava"));
|
||||
const semver_1 = require("semver");
|
||||
const sinon_1 = __importDefault(require("sinon"));
|
||||
const api = __importStar(require("./api-client"));
|
||||
const codeql_1 = require("./codeql");
|
||||
@@ -200,6 +201,7 @@ ava_1.default("load non-empty input", async (t) => {
|
||||
codeQLCmd: codeQL.getPath(),
|
||||
gitHubVersion,
|
||||
dbLocation: path.resolve(tmpDir, "codeql_databases"),
|
||||
packs: {},
|
||||
};
|
||||
const languages = "javascript";
|
||||
const configFilePath = createConfigFile(inputFileContents, tmpDir);
|
||||
@@ -557,6 +559,101 @@ ava_1.default("Unknown languages", async (t) => {
|
||||
}
|
||||
});
|
||||
});
|
||||
ava_1.default("Config specifies packages", async (t) => {
|
||||
return await util.withTmpDir(async (tmpDir) => {
|
||||
const codeQL = codeql_1.setCodeQL({
|
||||
async resolveQueries() {
|
||||
return {
|
||||
byLanguage: {},
|
||||
noDeclaredLanguage: {},
|
||||
multipleDeclaredLanguages: {},
|
||||
};
|
||||
},
|
||||
});
|
||||
const inputFileContents = `
|
||||
name: my config
|
||||
disable-default-queries: true
|
||||
packs:
|
||||
- a/b@1.2.3
|
||||
`;
|
||||
const configFile = path.join(tmpDir, "codeql-config.yaml");
|
||||
fs.writeFileSync(configFile, inputFileContents);
|
||||
const languages = "javascript";
|
||||
const { packs } = await configUtils.initConfig(languages, undefined, configFile, undefined, { owner: "github", repo: "example " }, tmpDir, tmpDir, codeQL, tmpDir, gitHubVersion, sampleApiDetails, logging_1.getRunnerLogger(true));
|
||||
t.deepEqual(packs, {
|
||||
[languages_1.Language.javascript]: [
|
||||
{
|
||||
packName: "a/b",
|
||||
version: semver_1.clean("1.2.3"),
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
ava_1.default("Config specifies packages for multiple languages", async (t) => {
|
||||
return await util.withTmpDir(async (tmpDir) => {
|
||||
const codeQL = codeql_1.setCodeQL({
|
||||
async resolveQueries() {
|
||||
return {
|
||||
byLanguage: {
|
||||
cpp: { "/foo/a.ql": {} },
|
||||
},
|
||||
noDeclaredLanguage: {},
|
||||
multipleDeclaredLanguages: {},
|
||||
};
|
||||
},
|
||||
});
|
||||
const inputFileContents = `
|
||||
name: my config
|
||||
disable-default-queries: true
|
||||
queries:
|
||||
- uses: ./foo
|
||||
packs:
|
||||
javascript:
|
||||
- a/b@1.2.3
|
||||
python:
|
||||
- c/d@1.2.3
|
||||
`;
|
||||
const configFile = path.join(tmpDir, "codeql-config.yaml");
|
||||
fs.writeFileSync(configFile, inputFileContents);
|
||||
fs.mkdirSync(path.join(tmpDir, "foo"));
|
||||
const languages = "javascript,python,cpp";
|
||||
const { packs, queries } = await configUtils.initConfig(languages, undefined, configFile, undefined, { owner: "github", repo: "example" }, tmpDir, tmpDir, codeQL, tmpDir, gitHubVersion, sampleApiDetails, logging_1.getRunnerLogger(true));
|
||||
t.deepEqual(packs, {
|
||||
[languages_1.Language.javascript]: [
|
||||
{
|
||||
packName: "a/b",
|
||||
version: semver_1.clean("1.2.3"),
|
||||
},
|
||||
],
|
||||
[languages_1.Language.python]: [
|
||||
{
|
||||
packName: "c/d",
|
||||
version: semver_1.clean("1.2.3"),
|
||||
},
|
||||
],
|
||||
});
|
||||
t.deepEqual(queries, {
|
||||
cpp: {
|
||||
builtin: [],
|
||||
custom: [
|
||||
{
|
||||
queries: ["/foo/a.ql"],
|
||||
searchPath: tmpDir,
|
||||
},
|
||||
],
|
||||
},
|
||||
javascript: {
|
||||
builtin: [],
|
||||
custom: [],
|
||||
},
|
||||
python: {
|
||||
builtin: [],
|
||||
custom: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
function doInvalidInputTest(testName, inputFileContents, expectedErrorMessageGenerator) {
|
||||
ava_1.default(`load invalid input - ${testName}`, async (t) => {
|
||||
return await util.withTmpDir(async (tmpDir) => {
|
||||
@@ -644,4 +741,54 @@ ava_1.default("path sanitisation", (t) => {
|
||||
// Trailing stars are stripped
|
||||
t.deepEqual(configUtils.validateAndSanitisePath("foo/**", propertyName, configFile, logging_1.getRunnerLogger(true)), "foo/");
|
||||
});
|
||||
/**
|
||||
* Test macro for ensuring the packs block is valid
|
||||
*/
|
||||
function parsePacksMacro(t, packsByLanguage, languages, expected) {
|
||||
t.deepEqual(configUtils.parsePacks(packsByLanguage, languages, "/a/b"), expected);
|
||||
}
|
||||
parsePacksMacro.title = (providedTitle) => `Parse Packs: ${providedTitle}`;
|
||||
/**
|
||||
* Test macro for testing when the packs block is invalid
|
||||
*/
|
||||
function parsePacksErrorMacro(t, packsByLanguage, languages, expected) {
|
||||
t.throws(() => {
|
||||
configUtils.parsePacks(packsByLanguage, languages, "/a/b");
|
||||
}, {
|
||||
message: expected,
|
||||
});
|
||||
}
|
||||
parsePacksErrorMacro.title = (providedTitle) => `Parse Packs Error: ${providedTitle}`;
|
||||
function invalidPackNameMacro(t, name) {
|
||||
parsePacksErrorMacro(t, { [languages_1.Language.cpp]: [name] }, [languages_1.Language.cpp], new RegExp(`The configuration file "/a/b" is invalid: property "packs" "${name}" is not a valid pack`));
|
||||
}
|
||||
invalidPackNameMacro.title = (_, arg) => `Invalid pack string: ${arg}`;
|
||||
ava_1.default("no packs", parsePacksMacro, {}, [], {});
|
||||
ava_1.default("two packs", parsePacksMacro, ["a/b", "c/d@1.2.3"], [languages_1.Language.cpp], {
|
||||
[languages_1.Language.cpp]: [
|
||||
{ packName: "a/b", version: undefined },
|
||||
{ packName: "c/d", version: semver_1.clean("1.2.3") },
|
||||
],
|
||||
});
|
||||
ava_1.default("two packs with language", parsePacksMacro, {
|
||||
[languages_1.Language.cpp]: ["a/b", "c/d@1.2.3"],
|
||||
[languages_1.Language.java]: ["d/e", "f/g@1.2.3"],
|
||||
}, [languages_1.Language.cpp, languages_1.Language.java, languages_1.Language.csharp], {
|
||||
[languages_1.Language.cpp]: [
|
||||
{ packName: "a/b", version: undefined },
|
||||
{ packName: "c/d", version: semver_1.clean("1.2.3") },
|
||||
],
|
||||
[languages_1.Language.java]: [
|
||||
{ packName: "d/e", version: undefined },
|
||||
{ packName: "f/g", version: semver_1.clean("1.2.3") },
|
||||
],
|
||||
});
|
||||
ava_1.default("no language", parsePacksErrorMacro, ["a/b@1.2.3"], [languages_1.Language.java, languages_1.Language.python], /The configuration file "\/a\/b" is invalid: property "packs" must split packages by language/);
|
||||
ava_1.default("invalid language", parsePacksErrorMacro, { [languages_1.Language.java]: ["c/d"] }, [languages_1.Language.cpp], /The configuration file "\/a\/b" is invalid: property "packs" has "java", but it is not one of the languages to analyze/);
|
||||
ava_1.default("not an array", parsePacksErrorMacro, { [languages_1.Language.cpp]: "c/d" }, [languages_1.Language.cpp], /The configuration file "\/a\/b" is invalid: property "packs" must be an array of non-empty strings/);
|
||||
ava_1.default(invalidPackNameMacro, "c"); // all packs require at least a scope and a name
|
||||
ava_1.default(invalidPackNameMacro, "c-/d");
|
||||
ava_1.default(invalidPackNameMacro, "-c/d");
|
||||
ava_1.default(invalidPackNameMacro, "c/d_d");
|
||||
ava_1.default(invalidPackNameMacro, "c/d@x");
|
||||
//# sourceMappingURL=config-utils.test.js.map
|
||||
File diff suppressed because one or more lines are too long
Generated
+3
-3
@@ -21,13 +21,13 @@ ava_1.default("ensure lines of code works for cpp and js", async (t) => {
|
||||
const results = await count_loc_1.countLoc(path.join(__dirname, "../tests/multi-language-repo"), [], [], [languages_1.Language.cpp, languages_1.Language.javascript], logging_1.getRunnerLogger(true));
|
||||
t.deepEqual(results, {
|
||||
cpp: 6,
|
||||
javascript: 3,
|
||||
javascript: 9,
|
||||
});
|
||||
});
|
||||
ava_1.default("ensure lines of code can handle undefined language", async (t) => {
|
||||
const results = await count_loc_1.countLoc(path.join(__dirname, "../tests/multi-language-repo"), [], [], [languages_1.Language.javascript, languages_1.Language.python, "hucairz"], logging_1.getRunnerLogger(true));
|
||||
t.deepEqual(results, {
|
||||
javascript: 3,
|
||||
javascript: 9,
|
||||
python: 5,
|
||||
});
|
||||
});
|
||||
@@ -54,7 +54,7 @@ ava_1.default("ensure lines of code can handle empty includes", async (t) => {
|
||||
ava_1.default("ensure lines of code can handle exclude", async (t) => {
|
||||
const results = await count_loc_1.countLoc(path.join(__dirname, "../tests/multi-language-repo"), [], ["**/*.py"], [languages_1.Language.javascript, languages_1.Language.python], logging_1.getRunnerLogger(true));
|
||||
t.deepEqual(results, {
|
||||
javascript: 3,
|
||||
javascript: 9,
|
||||
});
|
||||
});
|
||||
//# sourceMappingURL=count-loc.test.js.map
|
||||
Generated
+19
-9
@@ -16,6 +16,7 @@ const tab = "\t".charCodeAt(0);
|
||||
const space = " ".charCodeAt(0);
|
||||
const lf = "\n".charCodeAt(0);
|
||||
const cr = "\r".charCodeAt(0);
|
||||
const EOF = 65535;
|
||||
const BLOCK_SIZE = 100;
|
||||
const MOD = long_1.default.fromInt(37); // L
|
||||
// Compute the starting point for the hash mod
|
||||
@@ -36,9 +37,9 @@ function computeFirstMod() {
|
||||
* the hashes of the lines near the end of the file.
|
||||
*
|
||||
* @param callback function that is called with the line number (1-based) and hash for every line
|
||||
* @param input The file's contents
|
||||
* @param filepath The path to the file to hash
|
||||
*/
|
||||
function hash(callback, input) {
|
||||
async function hash(callback, filepath) {
|
||||
// A rolling view in to the input
|
||||
const window = Array(BLOCK_SIZE).fill(0);
|
||||
// If the character in the window is the start of a new line
|
||||
@@ -82,12 +83,11 @@ function hash(callback, input) {
|
||||
// as we go. Once we reach a point in the window again then we've processed
|
||||
// BLOCK_SIZE characters and if the last character at this point in the window
|
||||
// was the start of a line then we should output the hash for that line.
|
||||
for (let i = 0, len = input.length; i <= len; i++) {
|
||||
let current = i === len ? 65535 : input.charCodeAt(i);
|
||||
const processCharacter = function (current) {
|
||||
// skip tabs, spaces, and line feeds that come directly after a carriage return
|
||||
if (current === space || current === tab || (prevCR && current === lf)) {
|
||||
prevCR = false;
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
// replace CR with LF
|
||||
if (current === cr) {
|
||||
@@ -109,7 +109,14 @@ function hash(callback, input) {
|
||||
lineStart = true;
|
||||
}
|
||||
updateHash(current);
|
||||
};
|
||||
const readStream = fs.createReadStream(filepath, "utf8");
|
||||
for await (const data of readStream) {
|
||||
for (let i = 0; i < data.length; ++i) {
|
||||
processCharacter(data.charCodeAt(i));
|
||||
}
|
||||
}
|
||||
processCharacter(EOF);
|
||||
// Flush the remaining lines
|
||||
for (let i = 0; i < BLOCK_SIZE; i++) {
|
||||
if (lineNumbers[index] !== -1) {
|
||||
@@ -206,8 +213,8 @@ function resolveUriToFile(location, artifacts, checkoutPath, logger) {
|
||||
exports.resolveUriToFile = resolveUriToFile;
|
||||
// Compute fingerprints for results in the given sarif file
|
||||
// and return an updated sarif file contents.
|
||||
function addFingerprints(sarifContents, checkoutPath, logger) {
|
||||
var _a, _b;
|
||||
async function addFingerprints(sarifContents, checkoutPath, logger) {
|
||||
var _a, _b, _c, _d, _e;
|
||||
const sarif = JSON.parse(sarifContents);
|
||||
// Gather together results for the same file and construct
|
||||
// callbacks to accept hashes for that file and update the location
|
||||
@@ -222,6 +229,10 @@ function addFingerprints(sarifContents, checkoutPath, logger) {
|
||||
logger.debug(`Unable to compute fingerprint for invalid location: ${JSON.stringify(primaryLocation)}`);
|
||||
continue;
|
||||
}
|
||||
if (((_e = (_d = (_c = primaryLocation) === null || _c === void 0 ? void 0 : _c.physicalLocation) === null || _d === void 0 ? void 0 : _d.region) === null || _e === void 0 ? void 0 : _e.startLine) === undefined) {
|
||||
// Locations without a line number are unlikely to be source files
|
||||
continue;
|
||||
}
|
||||
const filepath = resolveUriToFile(primaryLocation.physicalLocation.artifactLocation, artifacts, checkoutPath, logger);
|
||||
if (!filepath) {
|
||||
continue;
|
||||
@@ -240,8 +251,7 @@ function addFingerprints(sarifContents, checkoutPath, logger) {
|
||||
c(lineNumber, hashValue);
|
||||
}
|
||||
};
|
||||
const fileContents = fs.readFileSync(filepath).toString();
|
||||
hash(teeCallback, fileContents);
|
||||
await hash(teeCallback, filepath);
|
||||
}
|
||||
return JSON.stringify(sarif);
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
Generated
+28
-23
@@ -16,28 +16,33 @@ const ava_1 = __importDefault(require("ava"));
|
||||
const fingerprints = __importStar(require("./fingerprints"));
|
||||
const logging_1 = require("./logging");
|
||||
const testing_utils_1 = require("./testing-utils");
|
||||
const util = __importStar(require("./util"));
|
||||
testing_utils_1.setupTests(ava_1.default);
|
||||
function testHash(t, input, expectedHashes) {
|
||||
let index = 0;
|
||||
const callback = function (lineNumber, hash) {
|
||||
t.is(lineNumber, index + 1);
|
||||
t.is(hash, expectedHashes[index]);
|
||||
index++;
|
||||
};
|
||||
fingerprints.hash(callback, input);
|
||||
t.is(index, input.split(/\r\n|\r|\n/).length);
|
||||
async function testHash(t, input, expectedHashes) {
|
||||
await util.withTmpDir(async (tmpDir) => {
|
||||
const tmpFile = path.resolve(tmpDir, "testfile");
|
||||
fs.writeFileSync(tmpFile, input);
|
||||
let index = 0;
|
||||
const callback = function (lineNumber, hash) {
|
||||
t.is(lineNumber, index + 1);
|
||||
t.is(hash, expectedHashes[index]);
|
||||
index++;
|
||||
};
|
||||
await fingerprints.hash(callback, tmpFile);
|
||||
t.is(index, input.split(/\r\n|\r|\n/).length);
|
||||
});
|
||||
}
|
||||
ava_1.default("hash", (t) => {
|
||||
ava_1.default("hash", async (t) => {
|
||||
// Try empty file
|
||||
testHash(t, "", ["c129715d7a2bc9a3:1"]);
|
||||
await testHash(t, "", ["c129715d7a2bc9a3:1"]);
|
||||
// Try various combinations of newline characters
|
||||
testHash(t, " a\nb\n \t\tc\n d", [
|
||||
await testHash(t, " a\nb\n \t\tc\n d", [
|
||||
"271789c17abda88f:1",
|
||||
"54703d4cd895b18:1",
|
||||
"180aee12dab6264:1",
|
||||
"a23a3dc5e078b07b:1",
|
||||
]);
|
||||
testHash(t, " hello; \t\nworld!!!\n\n\n \t\tGreetings\n End", [
|
||||
await testHash(t, " hello; \t\nworld!!!\n\n\n \t\tGreetings\n End", [
|
||||
"8b7cf3e952e7aeb2:1",
|
||||
"b1ae1287ec4718d9:1",
|
||||
"bff680108adb0fcc:1",
|
||||
@@ -45,7 +50,7 @@ ava_1.default("hash", (t) => {
|
||||
"b86d3392aea1be30:1",
|
||||
"e6ceba753e1a442:1",
|
||||
]);
|
||||
testHash(t, " hello; \t\nworld!!!\n\n\n \t\tGreetings\n End\n", [
|
||||
await testHash(t, " hello; \t\nworld!!!\n\n\n \t\tGreetings\n End\n", [
|
||||
"e9496ae3ebfced30:1",
|
||||
"fb7c023a8b9ccb3f:1",
|
||||
"ce8ba1a563dcdaca:1",
|
||||
@@ -54,7 +59,7 @@ ava_1.default("hash", (t) => {
|
||||
"c8e28b0b4002a3a0:1",
|
||||
"c129715d7a2bc9a3:1",
|
||||
]);
|
||||
testHash(t, " hello; \t\nworld!!!\r\r\r \t\tGreetings\r End\r", [
|
||||
await testHash(t, " hello; \t\nworld!!!\r\r\r \t\tGreetings\r End\r", [
|
||||
"e9496ae3ebfced30:1",
|
||||
"fb7c023a8b9ccb3f:1",
|
||||
"ce8ba1a563dcdaca:1",
|
||||
@@ -63,7 +68,7 @@ ava_1.default("hash", (t) => {
|
||||
"c8e28b0b4002a3a0:1",
|
||||
"c129715d7a2bc9a3:1",
|
||||
]);
|
||||
testHash(t, " hello; \t\r\nworld!!!\r\n\r\n\r\n \t\tGreetings\r\n End\r\n", [
|
||||
await testHash(t, " hello; \t\r\nworld!!!\r\n\r\n\r\n \t\tGreetings\r\n End\r\n", [
|
||||
"e9496ae3ebfced30:1",
|
||||
"fb7c023a8b9ccb3f:1",
|
||||
"ce8ba1a563dcdaca:1",
|
||||
@@ -72,7 +77,7 @@ ava_1.default("hash", (t) => {
|
||||
"c8e28b0b4002a3a0:1",
|
||||
"c129715d7a2bc9a3:1",
|
||||
]);
|
||||
testHash(t, " hello; \t\nworld!!!\r\n\n\r \t\tGreetings\r End\r\n", [
|
||||
await testHash(t, " hello; \t\nworld!!!\r\n\n\r \t\tGreetings\r End\r\n", [
|
||||
"e9496ae3ebfced30:1",
|
||||
"fb7c023a8b9ccb3f:1",
|
||||
"ce8ba1a563dcdaca:1",
|
||||
@@ -82,7 +87,7 @@ ava_1.default("hash", (t) => {
|
||||
"c129715d7a2bc9a3:1",
|
||||
]);
|
||||
// Try repeating line that will generate identical hashes
|
||||
testHash(t, "Lorem ipsum dolor sit amet.\n".repeat(10), [
|
||||
await testHash(t, "Lorem ipsum dolor sit amet.\n".repeat(10), [
|
||||
"a7f2ff13bc495cf2:1",
|
||||
"a7f2ff13bc495cf2:2",
|
||||
"a7f2ff13bc495cf2:3",
|
||||
@@ -95,7 +100,7 @@ ava_1.default("hash", (t) => {
|
||||
"cc97dc7b1d7d8f7b:1",
|
||||
"c129715d7a2bc9a3:1",
|
||||
]);
|
||||
testHash(t, "x = 2\nx = 1\nprint(x)\nx = 3\nprint(x)\nx = 4\nprint(x)\n", [
|
||||
await testHash(t, "x = 2\nx = 1\nprint(x)\nx = 3\nprint(x)\nx = 4\nprint(x)\n", [
|
||||
"e54938cc54b302f1:1",
|
||||
"bb609acbe9138d60:1",
|
||||
"1131fd5871777f34:1",
|
||||
@@ -150,7 +155,7 @@ ava_1.default("resolveUriToFile", (t) => {
|
||||
t.is(testResolveUriToFile(dirpath, undefined, []), undefined);
|
||||
t.is(testResolveUriToFile(`file://${dirpath}`, undefined, []), undefined);
|
||||
});
|
||||
ava_1.default("addFingerprints", (t) => {
|
||||
ava_1.default("addFingerprints", async (t) => {
|
||||
// Run an end-to-end test on a test file
|
||||
let input = fs
|
||||
.readFileSync(`${__dirname}/../src/testdata/fingerprinting.input.sarif`)
|
||||
@@ -163,9 +168,9 @@ ava_1.default("addFingerprints", (t) => {
|
||||
expected = JSON.stringify(JSON.parse(expected));
|
||||
// The URIs in the SARIF files resolve to files in the testdata directory
|
||||
const checkoutPath = path.normalize(`${__dirname}/../src/testdata`);
|
||||
t.deepEqual(fingerprints.addFingerprints(input, checkoutPath, logging_1.getRunnerLogger(true)), expected);
|
||||
t.deepEqual(await fingerprints.addFingerprints(input, checkoutPath, logging_1.getRunnerLogger(true)), expected);
|
||||
});
|
||||
ava_1.default("missingRegions", (t) => {
|
||||
ava_1.default("missingRegions", async (t) => {
|
||||
// Run an end-to-end test on a test file
|
||||
let input = fs
|
||||
.readFileSync(`${__dirname}/../src/testdata/fingerprinting2.input.sarif`)
|
||||
@@ -178,6 +183,6 @@ ava_1.default("missingRegions", (t) => {
|
||||
expected = JSON.stringify(JSON.parse(expected));
|
||||
// The URIs in the SARIF files resolve to files in the testdata directory
|
||||
const checkoutPath = path.normalize(`${__dirname}/../src/testdata`);
|
||||
t.deepEqual(fingerprints.addFingerprints(input, checkoutPath, logging_1.getRunnerLogger(true)), expected);
|
||||
t.deepEqual(await fingerprints.addFingerprints(input, checkoutPath, logging_1.getRunnerLogger(true)), expected);
|
||||
});
|
||||
//# sourceMappingURL=fingerprints.test.js.map
|
||||
File diff suppressed because one or more lines are too long
Generated
+2
-2
@@ -69,10 +69,10 @@ async function run() {
|
||||
if (!(await actions_util_1.sendStatusReport(await actions_util_1.createStatusReportBase("init", "starting", startedAt, workflowErrors)))) {
|
||||
return;
|
||||
}
|
||||
const initCodeQLResult = await init_1.initCodeQL(actions_util_1.getOptionalInput("tools"), apiDetails, util_1.getTemporaryDirectory(), actions_util_1.getToolCacheDirectory(), gitHubVersion.type, logger);
|
||||
const initCodeQLResult = await init_1.initCodeQL(actions_util_1.getOptionalInput("tools"), apiDetails, actions_util_1.getTemporaryDirectory(), actions_util_1.getToolCacheDirectory(), gitHubVersion.type, logger);
|
||||
codeql = initCodeQLResult.codeql;
|
||||
toolsVersion = initCodeQLResult.toolsVersion;
|
||||
config = await init_1.initConfig(actions_util_1.getOptionalInput("languages"), actions_util_1.getOptionalInput("queries"), actions_util_1.getOptionalInput("config-file"), actions_util_1.getOptionalInput("db-location"), repository_1.parseRepositoryNwo(util_1.getRequiredEnvParam("GITHUB_REPOSITORY")), util_1.getTemporaryDirectory(), util_1.getRequiredEnvParam("RUNNER_TOOL_CACHE"), codeql, util_1.getRequiredEnvParam("GITHUB_WORKSPACE"), gitHubVersion, apiDetails, logger);
|
||||
config = await init_1.initConfig(actions_util_1.getOptionalInput("languages"), actions_util_1.getOptionalInput("queries"), actions_util_1.getOptionalInput("config-file"), actions_util_1.getOptionalInput("db-location"), repository_1.parseRepositoryNwo(util_1.getRequiredEnvParam("GITHUB_REPOSITORY")), actions_util_1.getTemporaryDirectory(), util_1.getRequiredEnvParam("RUNNER_TOOL_CACHE"), codeql, util_1.getRequiredEnvParam("GITHUB_WORKSPACE"), gitHubVersion, apiDetails, logger);
|
||||
if (config.languages.includes(languages_1.Language.python) &&
|
||||
actions_util_1.getRequiredInput("setup-python-dependencies") === "true") {
|
||||
try {
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"init-action.js","sourceRoot":"","sources":["../src/init-action.ts"],"names":[],"mappings":"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"}
|
||||
{"version":3,"file":"init-action.js","sourceRoot":"","sources":["../src/init-action.ts"],"names":[],"mappings":"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"}
|
||||
Generated
+1
@@ -31,6 +31,7 @@ function getTestConfig(tmpDir) {
|
||||
codeQLCmd: "",
|
||||
gitHubVersion: { type: util.GitHubVariant.DOTCOM },
|
||||
dbLocation: path.resolve(tmpDir, "codeql_databases"),
|
||||
packs: {},
|
||||
};
|
||||
}
|
||||
// A very minimal setup
|
||||
|
||||
File diff suppressed because one or more lines are too long
Generated
+1
-1
@@ -243,7 +243,7 @@ async function uploadFiles(sarifFiles, repositoryNwo, commitOid, ref, analysisKe
|
||||
validateSarifFileSchema(file, logger);
|
||||
}
|
||||
let sarifPayload = combineSarifFiles(sarifFiles);
|
||||
sarifPayload = fingerprints.addFingerprints(sarifPayload, checkoutPath, logger);
|
||||
sarifPayload = await fingerprints.addFingerprints(sarifPayload, checkoutPath, logger);
|
||||
sarifPayload = populateRunAutomationDetails(sarifPayload, category, analysisKey, environment);
|
||||
const zippedSarif = zlib_1.default.gzipSync(sarifPayload).toString("base64");
|
||||
const checkoutURI = file_url_1.default(checkoutPath);
|
||||
|
||||
File diff suppressed because one or more lines are too long
Generated
+37
-36
@@ -335,42 +335,51 @@ var Mode;
|
||||
*/
|
||||
var EnvVar;
|
||||
(function (EnvVar) {
|
||||
// either 'actions' or 'runner'
|
||||
/**
|
||||
* The mode of the codeql-action, either 'actions' or 'runner'.
|
||||
*/
|
||||
EnvVar["RUN_MODE"] = "CODEQL_ACTION_RUN_MODE";
|
||||
// semver of this action
|
||||
/**
|
||||
* Semver of the codeql-action as specified in package.json.
|
||||
*/
|
||||
EnvVar["VERSION"] = "CODEQL_ACTION_VERSION";
|
||||
// if set to a truthy value, then the action might combine SARIF
|
||||
// output from several `interpret-results` runs for the same language
|
||||
/**
|
||||
* If set to a truthy value, then the codeql-action might combine SARIF
|
||||
* output from several `interpret-results` runs for the same Language.
|
||||
*/
|
||||
EnvVar["FEATURE_SARIF_COMBINE"] = "CODEQL_ACTION_FEATURE_SARIF_COMBINE";
|
||||
// if set to a truthy value, then the action will upload SARIF,
|
||||
// not the CLI
|
||||
/**
|
||||
* If set to the "true" string, then the codeql-action will upload SARIF,
|
||||
* not the cli.
|
||||
*/
|
||||
EnvVar["FEATURE_WILL_UPLOAD"] = "CODEQL_ACTION_FEATURE_WILL_UPLOAD";
|
||||
// if set to a truthy value, then the action is using its
|
||||
// own deprecated and non-standard way of scanning for multiple
|
||||
// languages
|
||||
/**
|
||||
* If set to the "true" string, then the codeql-action is using its
|
||||
* own deprecated and non-standard way of scanning for multiple
|
||||
* languages.
|
||||
*/
|
||||
EnvVar["FEATURE_MULTI_LANGUAGE"] = "CODEQL_ACTION_FEATURE_MULTI_LANGUAGE";
|
||||
// if set to a truthy value, then the action is using its
|
||||
// own sandwiched workflow mechanism
|
||||
/**
|
||||
* If set to the "true" string, then the codeql-action is using its
|
||||
* own sandwiched workflow mechanism
|
||||
*/
|
||||
EnvVar["FEATURE_SANDWICH"] = "CODEQL_ACTION_FEATURE_SANDWICH";
|
||||
})(EnvVar || (EnvVar = {}));
|
||||
function initializeEnvironment(mode, version) {
|
||||
// avoid accessing actions core when in runner mode
|
||||
if (mode === Mode.actions) {
|
||||
core.exportVariable(EnvVar.RUN_MODE, mode);
|
||||
core.exportVariable(EnvVar.VERSION, version);
|
||||
core.exportVariable(EnvVar.FEATURE_SARIF_COMBINE, "true");
|
||||
core.exportVariable(EnvVar.FEATURE_WILL_UPLOAD, "true");
|
||||
core.exportVariable(EnvVar.FEATURE_MULTI_LANGUAGE, "true");
|
||||
core.exportVariable(EnvVar.FEATURE_SANDWICH, "true");
|
||||
}
|
||||
else {
|
||||
process.env[EnvVar.RUN_MODE] = mode;
|
||||
process.env[EnvVar.VERSION] = version;
|
||||
process.env[EnvVar.FEATURE_SARIF_COMBINE] = "true";
|
||||
process.env[EnvVar.FEATURE_WILL_UPLOAD] = "true";
|
||||
process.env[EnvVar.FEATURE_MULTI_LANGUAGE] = "true";
|
||||
process.env[EnvVar.FEATURE_SANDWICH] = "true";
|
||||
}
|
||||
const exportVar = (name, value) => {
|
||||
if (mode === Mode.actions) {
|
||||
core.exportVariable(name, value);
|
||||
}
|
||||
else {
|
||||
process.env[name] = value;
|
||||
}
|
||||
};
|
||||
exportVar(EnvVar.RUN_MODE, mode);
|
||||
exportVar(EnvVar.VERSION, version);
|
||||
exportVar(EnvVar.FEATURE_SARIF_COMBINE, "true");
|
||||
exportVar(EnvVar.FEATURE_WILL_UPLOAD, "true");
|
||||
exportVar(EnvVar.FEATURE_MULTI_LANGUAGE, "true");
|
||||
exportVar(EnvVar.FEATURE_SANDWICH, "true");
|
||||
}
|
||||
exports.initializeEnvironment = initializeEnvironment;
|
||||
function getMode() {
|
||||
@@ -396,15 +405,7 @@ function getRequiredEnvParam(paramName) {
|
||||
if (value === undefined || value.length === 0) {
|
||||
throw new Error(`${paramName} environment variable must be set`);
|
||||
}
|
||||
core.debug(`${paramName}=${value}`);
|
||||
return value;
|
||||
}
|
||||
exports.getRequiredEnvParam = getRequiredEnvParam;
|
||||
function getTemporaryDirectory() {
|
||||
const value = process.env["CODEQL_ACTION_TEMP"];
|
||||
return value !== undefined && value !== ""
|
||||
? value
|
||||
: getRequiredEnvParam("RUNNER_TEMP");
|
||||
}
|
||||
exports.getTemporaryDirectory = getTemporaryDirectory;
|
||||
//# sourceMappingURL=util.js.map
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+963
-1203
File diff suppressed because it is too large
Load Diff
+9
@@ -0,0 +1,9 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright 2019 GitHub
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
+213
@@ -0,0 +1,213 @@
|
||||
# `@actions/artifact`
|
||||
|
||||
## Usage
|
||||
|
||||
You can use this package to interact with the actions artifacts.
|
||||
- [Upload an Artifact](#Upload-an-Artifact)
|
||||
- [Download a Single Artifact](#Download-a-Single-Artifact)
|
||||
- [Download All Artifacts](#Download-all-Artifacts)
|
||||
- [Additional Documentation](#Additional-Documentation)
|
||||
- [Contributions](#Contributions)
|
||||
|
||||
Relative paths and absolute paths are both allowed. Relative paths are rooted against the current working directory.
|
||||
|
||||
## Upload an Artifact
|
||||
|
||||
Method Name: `uploadArtifact`
|
||||
|
||||
#### Inputs
|
||||
- `name`
|
||||
- The name of the artifact that is being uploaded
|
||||
- Required
|
||||
- `files`
|
||||
- A list of file paths that describe what should be uploaded as part of the artifact
|
||||
- If a path is provided that does not exist, an error will be thrown
|
||||
- Can be absolute or relative. Internally everything is normalized and resolved
|
||||
- Required
|
||||
- `rootDirectory`
|
||||
- A file path that denotes the root directory of the files being uploaded. This path is used to strip the paths provided in `files` to control how they are uploaded and structured
|
||||
- If a file specified in `files` is not in the `rootDirectory`, an error will be thrown
|
||||
- Required
|
||||
- `options`
|
||||
- Extra options that allow for the customization of the upload behavior
|
||||
- Optional
|
||||
|
||||
#### Available Options
|
||||
|
||||
- `continueOnError`
|
||||
- Indicates if the artifact upload should continue in the event a file fails to upload. If there is a error during upload, a partial artifact will always be created and available for download at the end. The `size` reported will be the amount of storage that the user or org will be charged for the partial artifact.
|
||||
- If set to `false`, and an error is encountered, all other uploads will stop and any files that were queued will not be attempted to be uploaded. The partial artifact available will only include files up until the failure.
|
||||
- If set to `true` and an error is encountered, the failed file will be skipped and ignored and all other queued files will be attempted to be uploaded. There will be an artifact available for download at the end with everything excluding the file that failed to upload
|
||||
- Optional, defaults to `true` if not specified
|
||||
- `retentionDays`
|
||||
- Duration after which artifact will expire in days
|
||||
- Minimum value: 1
|
||||
- Maximum value: 90 unless changed by repository setting
|
||||
- If this is set to a greater value than the retention settings allowed, the retention on artifacts will be reduced to match the max value allowed on the server, and the upload process will continue. An input of 0 assumes default retention value.
|
||||
|
||||
#### Example using Absolute File Paths
|
||||
|
||||
```js
|
||||
const artifact = require('@actions/artifact');
|
||||
const artifactClient = artifact.create()
|
||||
const artifactName = 'my-artifact';
|
||||
const files = [
|
||||
'/home/user/files/plz-upload/file1.txt',
|
||||
'/home/user/files/plz-upload/file2.txt',
|
||||
'/home/user/files/plz-upload/dir/file3.txt'
|
||||
]
|
||||
const rootDirectory = '/home/user/files/plz-upload'
|
||||
const options = {
|
||||
continueOnError: true
|
||||
}
|
||||
|
||||
const uploadResult = await artifactClient.uploadArtifact(artifactName, files, rootDirectory, options)
|
||||
```
|
||||
|
||||
#### Example using Relative File Paths
|
||||
```js
|
||||
// Assuming the current working directory is /home/user/files/plz-upload
|
||||
const artifact = require('@actions/artifact');
|
||||
const artifactClient = artifact.create()
|
||||
const artifactName = 'my-artifact';
|
||||
const files = [
|
||||
'file1.txt',
|
||||
'file2.txt',
|
||||
'dir/file3.txt'
|
||||
]
|
||||
|
||||
const rootDirectory = '.' // Also possible to use __dirname
|
||||
const options = {
|
||||
continueOnError: false
|
||||
}
|
||||
|
||||
const uploadResponse = await artifactClient.uploadArtifact(artifactName, files, rootDirectory, options)
|
||||
```
|
||||
|
||||
#### Upload Result
|
||||
|
||||
The returned `UploadResponse` will contain the following information
|
||||
|
||||
- `artifactName`
|
||||
- The name of the artifact that was uploaded
|
||||
- `artifactItems`
|
||||
- A list of all files that describe what is uploaded if there are no errors encountered. Usually this will be equal to the inputted `files` with the exception of empty directories (will not be uploaded)
|
||||
- `size`
|
||||
- Total size of the artifact that was uploaded in bytes
|
||||
- `failedItems`
|
||||
- A list of items that were not uploaded successfully (this will include queued items that were not uploaded if `continueOnError` is set to false). This is a subset of `artifactItems`
|
||||
|
||||
## Download a Single Artifact
|
||||
|
||||
Method Name: `downloadArtifact`
|
||||
|
||||
#### Inputs
|
||||
- `name`
|
||||
- The name of the artifact to download
|
||||
- Required
|
||||
- `path`
|
||||
- Path that denotes where the artifact will be downloaded to
|
||||
- Optional. Defaults to the GitHub workspace directory(`$GITHUB_WORKSPACE`) if not specified
|
||||
- `options`
|
||||
- Extra options that allow for the customization of the download behavior
|
||||
- Optional
|
||||
|
||||
|
||||
#### Available Options
|
||||
|
||||
- `createArtifactFolder`
|
||||
- Specifies if a folder (the artifact name) is created for the artifact that is downloaded (contents downloaded into this folder),
|
||||
- Optional. Defaults to false if not specified
|
||||
|
||||
#### Example
|
||||
|
||||
```js
|
||||
const artifact = require('@actions/artifact');
|
||||
const artifactClient = artifact.create()
|
||||
const artifactName = 'my-artifact';
|
||||
const path = 'some/directory'
|
||||
const options = {
|
||||
createArtifactFolder: false
|
||||
}
|
||||
|
||||
const downloadResponse = await artifactClient.downloadArtifact(artifactName, path, options)
|
||||
|
||||
// Post download, the directory structure will look like this
|
||||
/some
|
||||
/directory
|
||||
/file1.txt
|
||||
/file2.txt
|
||||
/dir
|
||||
/file3.txt
|
||||
|
||||
// If createArtifactFolder is set to true, the directory structure will look like this
|
||||
/some
|
||||
/directory
|
||||
/my-artifact
|
||||
/file1.txt
|
||||
/file2.txt
|
||||
/dir
|
||||
/file3.txt
|
||||
```
|
||||
|
||||
#### Download Response
|
||||
|
||||
The returned `DownloadResponse` will contain the following information
|
||||
|
||||
- `artifactName`
|
||||
- The name of the artifact that was downloaded
|
||||
- `downloadPath`
|
||||
- The full Path to where the artifact was downloaded
|
||||
|
||||
|
||||
## Download All Artifacts
|
||||
|
||||
Method Name: `downloadAllArtifacts`
|
||||
|
||||
#### Inputs
|
||||
- `path`
|
||||
- Path that denotes where the artifact will be downloaded to
|
||||
- Optional. Defaults to the GitHub workspace directory(`$GITHUB_WORKSPACE`) if not specified
|
||||
|
||||
```js
|
||||
const artifact = require('@actions/artifact');
|
||||
const artifactClient = artifact.create();
|
||||
const downloadResponse = await artifactClient.downloadAllArtifacts();
|
||||
|
||||
// output result
|
||||
for (response in downloadResponse) {
|
||||
console.log(response.artifactName);
|
||||
console.log(response.downloadPath);
|
||||
}
|
||||
```
|
||||
|
||||
Because there are multiple artifacts, an extra directory (denoted by the name of the artifact) will be created for each artifact in the path. With 2 artifacts(`my-artifact-1` and `my-artifact-2` for example) and the default path, the directory structure will be as follows:
|
||||
```js
|
||||
/GITHUB_WORKSPACE
|
||||
/my-artifact-1
|
||||
/ .. contents of `my-artifact-1`
|
||||
/my-artifact-2
|
||||
/ .. contents of `my-artifact-2`
|
||||
```
|
||||
|
||||
#### Download Result
|
||||
|
||||
An array will be returned that describes the results for downloading all artifacts. The number of items in the array indicates the number of artifacts that were downloaded.
|
||||
|
||||
Each artifact will have the same `DownloadResponse` as if it was individually downloaded
|
||||
- `artifactName`
|
||||
- The name of the artifact that was downloaded
|
||||
- `downloadPath`
|
||||
- The full Path to where the artifact was downloaded
|
||||
|
||||
## Additional Documentation
|
||||
|
||||
Check out [additional-information](docs/additional-information.md) for extra documentation around usage, restrictions and behavior.
|
||||
|
||||
Check out [implementation-details](docs/implementation-details.md) for extra information about the implementation of this package.
|
||||
|
||||
## Contributions
|
||||
|
||||
See [contributor guidelines](https://github.com/actions/toolkit/blob/main/.github/CONTRIBUTING.md) for general guidelines and information about toolkit contributions.
|
||||
|
||||
For contributions related to this package, see [artifact contributions](CONTRIBUTIONS.md) for more information.
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import { UploadOptions } from './internal/upload-options';
|
||||
import { UploadResponse } from './internal/upload-response';
|
||||
import { DownloadOptions } from './internal/download-options';
|
||||
import { DownloadResponse } from './internal/download-response';
|
||||
import { ArtifactClient } from './internal/artifact-client';
|
||||
export { ArtifactClient, UploadResponse, UploadOptions, DownloadResponse, DownloadOptions };
|
||||
/**
|
||||
* Constructs an ArtifactClient
|
||||
*/
|
||||
export declare function create(): ArtifactClient;
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const artifact_client_1 = require("./internal/artifact-client");
|
||||
/**
|
||||
* Constructs an ArtifactClient
|
||||
*/
|
||||
function create() {
|
||||
return artifact_client_1.DefaultArtifactClient.create();
|
||||
}
|
||||
exports.create = create;
|
||||
//# sourceMappingURL=artifact-client.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"artifact-client.js","sourceRoot":"","sources":["../src/artifact-client.ts"],"names":[],"mappings":";;AAIA,gEAAgF;AAUhF;;GAEG;AACH,SAAgB,MAAM;IACpB,OAAO,uCAAqB,CAAC,MAAM,EAAE,CAAA;AACvC,CAAC;AAFD,wBAEC"}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
import { UploadResponse } from './upload-response';
|
||||
import { UploadOptions } from './upload-options';
|
||||
import { DownloadOptions } from './download-options';
|
||||
import { DownloadResponse } from './download-response';
|
||||
export interface ArtifactClient {
|
||||
/**
|
||||
* Uploads an artifact
|
||||
*
|
||||
* @param name the name of the artifact, required
|
||||
* @param files a list of absolute or relative paths that denote what files should be uploaded
|
||||
* @param rootDirectory an absolute or relative file path that denotes the root parent directory of the files being uploaded
|
||||
* @param options extra options for customizing the upload behavior
|
||||
* @returns single UploadInfo object
|
||||
*/
|
||||
uploadArtifact(name: string, files: string[], rootDirectory: string, options?: UploadOptions): Promise<UploadResponse>;
|
||||
/**
|
||||
* Downloads a single artifact associated with a run
|
||||
*
|
||||
* @param name the name of the artifact being downloaded
|
||||
* @param path optional path that denotes where the artifact will be downloaded to
|
||||
* @param options extra options that allow for the customization of the download behavior
|
||||
*/
|
||||
downloadArtifact(name: string, path?: string, options?: DownloadOptions): Promise<DownloadResponse>;
|
||||
/**
|
||||
* Downloads all artifacts associated with a run. Because there are multiple artifacts being downloaded, a folder will be created for each one in the specified or default directory
|
||||
* @param path optional path that denotes where the artifacts will be downloaded to
|
||||
*/
|
||||
downloadAllArtifacts(path?: string): Promise<DownloadResponse[]>;
|
||||
}
|
||||
export declare class DefaultArtifactClient implements ArtifactClient {
|
||||
/**
|
||||
* Constructs a DefaultArtifactClient
|
||||
*/
|
||||
static create(): DefaultArtifactClient;
|
||||
/**
|
||||
* Uploads an artifact
|
||||
*/
|
||||
uploadArtifact(name: string, files: string[], rootDirectory: string, options?: UploadOptions | undefined): Promise<UploadResponse>;
|
||||
downloadArtifact(name: string, path?: string | undefined, options?: DownloadOptions | undefined): Promise<DownloadResponse>;
|
||||
downloadAllArtifacts(path?: string | undefined): Promise<DownloadResponse[]>;
|
||||
}
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
||||
result["default"] = mod;
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const core = __importStar(require("@actions/core"));
|
||||
const upload_specification_1 = require("./upload-specification");
|
||||
const upload_http_client_1 = require("./upload-http-client");
|
||||
const utils_1 = require("./utils");
|
||||
const download_http_client_1 = require("./download-http-client");
|
||||
const download_specification_1 = require("./download-specification");
|
||||
const config_variables_1 = require("./config-variables");
|
||||
const path_1 = require("path");
|
||||
class DefaultArtifactClient {
|
||||
/**
|
||||
* Constructs a DefaultArtifactClient
|
||||
*/
|
||||
static create() {
|
||||
return new DefaultArtifactClient();
|
||||
}
|
||||
/**
|
||||
* Uploads an artifact
|
||||
*/
|
||||
uploadArtifact(name, files, rootDirectory, options) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
utils_1.checkArtifactName(name);
|
||||
// Get specification for the files being uploaded
|
||||
const uploadSpecification = upload_specification_1.getUploadSpecification(name, rootDirectory, files);
|
||||
const uploadResponse = {
|
||||
artifactName: name,
|
||||
artifactItems: [],
|
||||
size: 0,
|
||||
failedItems: []
|
||||
};
|
||||
const uploadHttpClient = new upload_http_client_1.UploadHttpClient();
|
||||
if (uploadSpecification.length === 0) {
|
||||
core.warning(`No files found that can be uploaded`);
|
||||
}
|
||||
else {
|
||||
// Create an entry for the artifact in the file container
|
||||
const response = yield uploadHttpClient.createArtifactInFileContainer(name, options);
|
||||
if (!response.fileContainerResourceUrl) {
|
||||
core.debug(response.toString());
|
||||
throw new Error('No URL provided by the Artifact Service to upload an artifact to');
|
||||
}
|
||||
core.debug(`Upload Resource URL: ${response.fileContainerResourceUrl}`);
|
||||
// Upload each of the files that were found concurrently
|
||||
const uploadResult = yield uploadHttpClient.uploadArtifactToFileContainer(response.fileContainerResourceUrl, uploadSpecification, options);
|
||||
// Update the size of the artifact to indicate we are done uploading
|
||||
// The uncompressed size is used for display when downloading a zip of the artifact from the UI
|
||||
yield uploadHttpClient.patchArtifactSize(uploadResult.totalSize, name);
|
||||
core.info(`Finished uploading artifact ${name}. Reported size is ${uploadResult.uploadSize} bytes. There were ${uploadResult.failedItems.length} items that failed to upload`);
|
||||
uploadResponse.artifactItems = uploadSpecification.map(item => item.absoluteFilePath);
|
||||
uploadResponse.size = uploadResult.uploadSize;
|
||||
uploadResponse.failedItems = uploadResult.failedItems;
|
||||
}
|
||||
return uploadResponse;
|
||||
});
|
||||
}
|
||||
downloadArtifact(name, path, options) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const downloadHttpClient = new download_http_client_1.DownloadHttpClient();
|
||||
const artifacts = yield downloadHttpClient.listArtifacts();
|
||||
if (artifacts.count === 0) {
|
||||
throw new Error(`Unable to find any artifacts for the associated workflow`);
|
||||
}
|
||||
const artifactToDownload = artifacts.value.find(artifact => {
|
||||
return artifact.name === name;
|
||||
});
|
||||
if (!artifactToDownload) {
|
||||
throw new Error(`Unable to find an artifact with the name: ${name}`);
|
||||
}
|
||||
const items = yield downloadHttpClient.getContainerItems(artifactToDownload.name, artifactToDownload.fileContainerResourceUrl);
|
||||
if (!path) {
|
||||
path = config_variables_1.getWorkSpaceDirectory();
|
||||
}
|
||||
path = path_1.normalize(path);
|
||||
path = path_1.resolve(path);
|
||||
// During upload, empty directories are rejected by the remote server so there should be no artifacts that consist of only empty directories
|
||||
const downloadSpecification = download_specification_1.getDownloadSpecification(name, items.value, path, (options === null || options === void 0 ? void 0 : options.createArtifactFolder) || false);
|
||||
if (downloadSpecification.filesToDownload.length === 0) {
|
||||
core.info(`No downloadable files were found for the artifact: ${artifactToDownload.name}`);
|
||||
}
|
||||
else {
|
||||
// Create all necessary directories recursively before starting any download
|
||||
yield utils_1.createDirectoriesForArtifact(downloadSpecification.directoryStructure);
|
||||
core.info('Directory structure has been setup for the artifact');
|
||||
yield utils_1.createEmptyFilesForArtifact(downloadSpecification.emptyFilesToCreate);
|
||||
yield downloadHttpClient.downloadSingleArtifact(downloadSpecification.filesToDownload);
|
||||
}
|
||||
return {
|
||||
artifactName: name,
|
||||
downloadPath: downloadSpecification.rootDownloadLocation
|
||||
};
|
||||
});
|
||||
}
|
||||
downloadAllArtifacts(path) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const downloadHttpClient = new download_http_client_1.DownloadHttpClient();
|
||||
const response = [];
|
||||
const artifacts = yield downloadHttpClient.listArtifacts();
|
||||
if (artifacts.count === 0) {
|
||||
core.info('Unable to find any artifacts for the associated workflow');
|
||||
return response;
|
||||
}
|
||||
if (!path) {
|
||||
path = config_variables_1.getWorkSpaceDirectory();
|
||||
}
|
||||
path = path_1.normalize(path);
|
||||
path = path_1.resolve(path);
|
||||
let downloadedArtifacts = 0;
|
||||
while (downloadedArtifacts < artifacts.count) {
|
||||
const currentArtifactToDownload = artifacts.value[downloadedArtifacts];
|
||||
downloadedArtifacts += 1;
|
||||
// Get container entries for the specific artifact
|
||||
const items = yield downloadHttpClient.getContainerItems(currentArtifactToDownload.name, currentArtifactToDownload.fileContainerResourceUrl);
|
||||
const downloadSpecification = download_specification_1.getDownloadSpecification(currentArtifactToDownload.name, items.value, path, true);
|
||||
if (downloadSpecification.filesToDownload.length === 0) {
|
||||
core.info(`No downloadable files were found for any artifact ${currentArtifactToDownload.name}`);
|
||||
}
|
||||
else {
|
||||
yield utils_1.createDirectoriesForArtifact(downloadSpecification.directoryStructure);
|
||||
yield utils_1.createEmptyFilesForArtifact(downloadSpecification.emptyFilesToCreate);
|
||||
yield downloadHttpClient.downloadSingleArtifact(downloadSpecification.filesToDownload);
|
||||
}
|
||||
response.push({
|
||||
artifactName: currentArtifactToDownload.name,
|
||||
downloadPath: downloadSpecification.rootDownloadLocation
|
||||
});
|
||||
}
|
||||
return response;
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.DefaultArtifactClient = DefaultArtifactClient;
|
||||
//# sourceMappingURL=artifact-client.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"artifact-client.js","sourceRoot":"","sources":["../../src/internal/artifact-client.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAAA,oDAAqC;AACrC,iEAG+B;AAC/B,6DAAqD;AAKrD,mCAIgB;AAChB,iEAAyD;AACzD,qEAAiE;AACjE,yDAAwD;AACxD,+BAAuC;AAuCvC,MAAa,qBAAqB;IAChC;;OAEG;IACH,MAAM,CAAC,MAAM;QACX,OAAO,IAAI,qBAAqB,EAAE,CAAA;IACpC,CAAC;IAED;;OAEG;IACG,cAAc,CAClB,IAAY,EACZ,KAAe,EACf,aAAqB,EACrB,OAAmC;;YAEnC,yBAAiB,CAAC,IAAI,CAAC,CAAA;YAEvB,iDAAiD;YACjD,MAAM,mBAAmB,GAA0B,6CAAsB,CACvE,IAAI,EACJ,aAAa,EACb,KAAK,CACN,CAAA;YACD,MAAM,cAAc,GAAmB;gBACrC,YAAY,EAAE,IAAI;gBAClB,aAAa,EAAE,EAAE;gBACjB,IAAI,EAAE,CAAC;gBACP,WAAW,EAAE,EAAE;aAChB,CAAA;YAED,MAAM,gBAAgB,GAAG,IAAI,qCAAgB,EAAE,CAAA;YAE/C,IAAI,mBAAmB,CAAC,MAAM,KAAK,CAAC,EAAE;gBACpC,IAAI,CAAC,OAAO,CAAC,qCAAqC,CAAC,CAAA;aACpD;iBAAM;gBACL,yDAAyD;gBACzD,MAAM,QAAQ,GAAG,MAAM,gBAAgB,CAAC,6BAA6B,CACnE,IAAI,EACJ,OAAO,CACR,CAAA;gBACD,IAAI,CAAC,QAAQ,CAAC,wBAAwB,EAAE;oBACtC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAA;oBAC/B,MAAM,IAAI,KAAK,CACb,kEAAkE,CACnE,CAAA;iBACF;gBACD,IAAI,CAAC,KAAK,CAAC,wBAAwB,QAAQ,CAAC,wBAAwB,EAAE,CAAC,CAAA;gBAEvE,wDAAwD;gBACxD,MAAM,YAAY,GAAG,MAAM,gBAAgB,CAAC,6BAA6B,CACvE,QAAQ,CAAC,wBAAwB,EACjC,mBAAmB,EACnB,OAAO,CACR,CAAA;gBAED,oEAAoE;gBACpE,+FAA+F;gBAC/F,MAAM,gBAAgB,CAAC,iBAAiB,CAAC,YAAY,CAAC,SAAS,EAAE,IAAI,CAAC,CAAA;gBAEtE,IAAI,CAAC,IAAI,CACP,+BAA+B,IAAI,sBAAsB,YAAY,CAAC,UAAU,sBAAsB,YAAY,CAAC,WAAW,CAAC,MAAM,8BAA8B,CACpK,CAAA;gBAED,cAAc,CAAC,aAAa,GAAG,mBAAmB,CAAC,GAAG,CACpD,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAC9B,CAAA;gBACD,cAAc,CAAC,IAAI,GAAG,YAAY,CAAC,UAAU,CAAA;gBAC7C,cAAc,CAAC,WAAW,GAAG,YAAY,CAAC,WAAW,CAAA;aACtD;YACD,OAAO,cAAc,CAAA;QACvB,CAAC;KAAA;IAEK,gBAAgB,CACpB,IAAY,EACZ,IAAyB,EACzB,OAAqC;;YAErC,MAAM,kBAAkB,GAAG,IAAI,yCAAkB,EAAE,CAAA;YAEnD,MAAM,SAAS,GAAG,MAAM,kBAAkB,CAAC,aAAa,EAAE,CAAA;YAC1D,IAAI,SAAS,CAAC,KAAK,KAAK,CAAC,EAAE;gBACzB,MAAM,IAAI,KAAK,CACb,0DAA0D,CAC3D,CAAA;aACF;YAED,MAAM,kBAAkB,GAAG,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE;gBACzD,OAAO,QAAQ,CAAC,IAAI,KAAK,IAAI,CAAA;YAC/B,CAAC,CAAC,CAAA;YACF,IAAI,CAAC,kBAAkB,EAAE;gBACvB,MAAM,IAAI,KAAK,CAAC,6CAA6C,IAAI,EAAE,CAAC,CAAA;aACrE;YAED,MAAM,KAAK,GAAG,MAAM,kBAAkB,CAAC,iBAAiB,CACtD,kBAAkB,CAAC,IAAI,EACvB,kBAAkB,CAAC,wBAAwB,CAC5C,CAAA;YAED,IAAI,CAAC,IAAI,EAAE;gBACT,IAAI,GAAG,wCAAqB,EAAE,CAAA;aAC/B;YACD,IAAI,GAAG,gBAAS,CAAC,IAAI,CAAC,CAAA;YACtB,IAAI,GAAG,cAAO,CAAC,IAAI,CAAC,CAAA;YAEpB,4IAA4I;YAC5I,MAAM,qBAAqB,GAAG,iDAAwB,CACpD,IAAI,EACJ,KAAK,CAAC,KAAK,EACX,IAAI,EACJ,CAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,oBAAoB,KAAI,KAAK,CACvC,CAAA;YAED,IAAI,qBAAqB,CAAC,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE;gBACtD,IAAI,CAAC,IAAI,CACP,sDAAsD,kBAAkB,CAAC,IAAI,EAAE,CAChF,CAAA;aACF;iBAAM;gBACL,4EAA4E;gBAC5E,MAAM,oCAA4B,CAChC,qBAAqB,CAAC,kBAAkB,CACzC,CAAA;gBACD,IAAI,CAAC,IAAI,CAAC,qDAAqD,CAAC,CAAA;gBAChE,MAAM,mCAA2B,CAC/B,qBAAqB,CAAC,kBAAkB,CACzC,CAAA;gBACD,MAAM,kBAAkB,CAAC,sBAAsB,CAC7C,qBAAqB,CAAC,eAAe,CACtC,CAAA;aACF;YAED,OAAO;gBACL,YAAY,EAAE,IAAI;gBAClB,YAAY,EAAE,qBAAqB,CAAC,oBAAoB;aACzD,CAAA;QACH,CAAC;KAAA;IAEK,oBAAoB,CACxB,IAAyB;;YAEzB,MAAM,kBAAkB,GAAG,IAAI,yCAAkB,EAAE,CAAA;YAEnD,MAAM,QAAQ,GAAuB,EAAE,CAAA;YACvC,MAAM,SAAS,GAAG,MAAM,kBAAkB,CAAC,aAAa,EAAE,CAAA;YAC1D,IAAI,SAAS,CAAC,KAAK,KAAK,CAAC,EAAE;gBACzB,IAAI,CAAC,IAAI,CAAC,0DAA0D,CAAC,CAAA;gBACrE,OAAO,QAAQ,CAAA;aAChB;YAED,IAAI,CAAC,IAAI,EAAE;gBACT,IAAI,GAAG,wCAAqB,EAAE,CAAA;aAC/B;YACD,IAAI,GAAG,gBAAS,CAAC,IAAI,CAAC,CAAA;YACtB,IAAI,GAAG,cAAO,CAAC,IAAI,CAAC,CAAA;YAEpB,IAAI,mBAAmB,GAAG,CAAC,CAAA;YAC3B,OAAO,mBAAmB,GAAG,SAAS,CAAC,KAAK,EAAE;gBAC5C,MAAM,yBAAyB,GAAG,SAAS,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAA;gBACtE,mBAAmB,IAAI,CAAC,CAAA;gBAExB,kDAAkD;gBAClD,MAAM,KAAK,GAAG,MAAM,kBAAkB,CAAC,iBAAiB,CACtD,yBAAyB,CAAC,IAAI,EAC9B,yBAAyB,CAAC,wBAAwB,CACnD,CAAA;gBAED,MAAM,qBAAqB,GAAG,iDAAwB,CACpD,yBAAyB,CAAC,IAAI,EAC9B,KAAK,CAAC,KAAK,EACX,IAAI,EACJ,IAAI,CACL,CAAA;gBACD,IAAI,qBAAqB,CAAC,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE;oBACtD,IAAI,CAAC,IAAI,CACP,qDAAqD,yBAAyB,CAAC,IAAI,EAAE,CACtF,CAAA;iBACF;qBAAM;oBACL,MAAM,oCAA4B,CAChC,qBAAqB,CAAC,kBAAkB,CACzC,CAAA;oBACD,MAAM,mCAA2B,CAC/B,qBAAqB,CAAC,kBAAkB,CACzC,CAAA;oBACD,MAAM,kBAAkB,CAAC,sBAAsB,CAC7C,qBAAqB,CAAC,eAAe,CACtC,CAAA;iBACF;gBAED,QAAQ,CAAC,IAAI,CAAC;oBACZ,YAAY,EAAE,yBAAyB,CAAC,IAAI;oBAC5C,YAAY,EAAE,qBAAqB,CAAC,oBAAoB;iBACzD,CAAC,CAAA;aACH;YACD,OAAO,QAAQ,CAAA;QACjB,CAAC;KAAA;CACF;AApMD,sDAoMC"}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
export declare function getUploadFileConcurrency(): number;
|
||||
export declare function getUploadChunkSize(): number;
|
||||
export declare function getRetryLimit(): number;
|
||||
export declare function getRetryMultiplier(): number;
|
||||
export declare function getInitialRetryIntervalInMilliseconds(): number;
|
||||
export declare function getDownloadFileConcurrency(): number;
|
||||
export declare function getRuntimeToken(): string;
|
||||
export declare function getRuntimeUrl(): string;
|
||||
export declare function getWorkFlowRunId(): string;
|
||||
export declare function getWorkSpaceDirectory(): string;
|
||||
export declare function getRetentionDays(): string | undefined;
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
// The number of concurrent uploads that happens at the same time
|
||||
function getUploadFileConcurrency() {
|
||||
return 2;
|
||||
}
|
||||
exports.getUploadFileConcurrency = getUploadFileConcurrency;
|
||||
// When uploading large files that can't be uploaded with a single http call, this controls
|
||||
// the chunk size that is used during upload
|
||||
function getUploadChunkSize() {
|
||||
return 8 * 1024 * 1024; // 8 MB Chunks
|
||||
}
|
||||
exports.getUploadChunkSize = getUploadChunkSize;
|
||||
// The maximum number of retries that can be attempted before an upload or download fails
|
||||
function getRetryLimit() {
|
||||
return 5;
|
||||
}
|
||||
exports.getRetryLimit = getRetryLimit;
|
||||
// With exponential backoff, the larger the retry count, the larger the wait time before another attempt
|
||||
// The retry multiplier controls by how much the backOff time increases depending on the number of retries
|
||||
function getRetryMultiplier() {
|
||||
return 1.5;
|
||||
}
|
||||
exports.getRetryMultiplier = getRetryMultiplier;
|
||||
// The initial wait time if an upload or download fails and a retry is being attempted for the first time
|
||||
function getInitialRetryIntervalInMilliseconds() {
|
||||
return 3000;
|
||||
}
|
||||
exports.getInitialRetryIntervalInMilliseconds = getInitialRetryIntervalInMilliseconds;
|
||||
// The number of concurrent downloads that happens at the same time
|
||||
function getDownloadFileConcurrency() {
|
||||
return 2;
|
||||
}
|
||||
exports.getDownloadFileConcurrency = getDownloadFileConcurrency;
|
||||
function getRuntimeToken() {
|
||||
const token = process.env['ACTIONS_RUNTIME_TOKEN'];
|
||||
if (!token) {
|
||||
throw new Error('Unable to get ACTIONS_RUNTIME_TOKEN env variable');
|
||||
}
|
||||
return token;
|
||||
}
|
||||
exports.getRuntimeToken = getRuntimeToken;
|
||||
function getRuntimeUrl() {
|
||||
const runtimeUrl = process.env['ACTIONS_RUNTIME_URL'];
|
||||
if (!runtimeUrl) {
|
||||
throw new Error('Unable to get ACTIONS_RUNTIME_URL env variable');
|
||||
}
|
||||
return runtimeUrl;
|
||||
}
|
||||
exports.getRuntimeUrl = getRuntimeUrl;
|
||||
function getWorkFlowRunId() {
|
||||
const workFlowRunId = process.env['GITHUB_RUN_ID'];
|
||||
if (!workFlowRunId) {
|
||||
throw new Error('Unable to get GITHUB_RUN_ID env variable');
|
||||
}
|
||||
return workFlowRunId;
|
||||
}
|
||||
exports.getWorkFlowRunId = getWorkFlowRunId;
|
||||
function getWorkSpaceDirectory() {
|
||||
const workspaceDirectory = process.env['GITHUB_WORKSPACE'];
|
||||
if (!workspaceDirectory) {
|
||||
throw new Error('Unable to get GITHUB_WORKSPACE env variable');
|
||||
}
|
||||
return workspaceDirectory;
|
||||
}
|
||||
exports.getWorkSpaceDirectory = getWorkSpaceDirectory;
|
||||
function getRetentionDays() {
|
||||
return process.env['GITHUB_RETENTION_DAYS'];
|
||||
}
|
||||
exports.getRetentionDays = getRetentionDays;
|
||||
//# sourceMappingURL=config-variables.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"config-variables.js","sourceRoot":"","sources":["../../src/internal/config-variables.ts"],"names":[],"mappings":";;AAAA,iEAAiE;AACjE,SAAgB,wBAAwB;IACtC,OAAO,CAAC,CAAA;AACV,CAAC;AAFD,4DAEC;AAED,2FAA2F;AAC3F,4CAA4C;AAC5C,SAAgB,kBAAkB;IAChC,OAAO,CAAC,GAAG,IAAI,GAAG,IAAI,CAAA,CAAC,cAAc;AACvC,CAAC;AAFD,gDAEC;AAED,yFAAyF;AACzF,SAAgB,aAAa;IAC3B,OAAO,CAAC,CAAA;AACV,CAAC;AAFD,sCAEC;AAED,wGAAwG;AACxG,0GAA0G;AAC1G,SAAgB,kBAAkB;IAChC,OAAO,GAAG,CAAA;AACZ,CAAC;AAFD,gDAEC;AAED,yGAAyG;AACzG,SAAgB,qCAAqC;IACnD,OAAO,IAAI,CAAA;AACb,CAAC;AAFD,sFAEC;AAED,mEAAmE;AACnE,SAAgB,0BAA0B;IACxC,OAAO,CAAC,CAAA;AACV,CAAC;AAFD,gEAEC;AAED,SAAgB,eAAe;IAC7B,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAA;IAClD,IAAI,CAAC,KAAK,EAAE;QACV,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAA;KACpE;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAND,0CAMC;AAED,SAAgB,aAAa;IAC3B,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAA;IACrD,IAAI,CAAC,UAAU,EAAE;QACf,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAA;KAClE;IACD,OAAO,UAAU,CAAA;AACnB,CAAC;AAND,sCAMC;AAED,SAAgB,gBAAgB;IAC9B,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAA;IAClD,IAAI,CAAC,aAAa,EAAE;QAClB,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAA;KAC5D;IACD,OAAO,aAAa,CAAA;AACtB,CAAC;AAND,4CAMC;AAED,SAAgB,qBAAqB;IACnC,MAAM,kBAAkB,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAA;IAC1D,IAAI,CAAC,kBAAkB,EAAE;QACvB,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAA;KAC/D;IACD,OAAO,kBAAkB,CAAA;AAC3B,CAAC;AAND,sDAMC;AAED,SAAgB,gBAAgB;IAC9B,OAAO,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAA;AAC7C,CAAC;AAFD,4CAEC"}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
export interface ArtifactResponse {
|
||||
containerId: string;
|
||||
size: number;
|
||||
signedContent: string;
|
||||
fileContainerResourceUrl: string;
|
||||
type: string;
|
||||
name: string;
|
||||
url: string;
|
||||
}
|
||||
export interface CreateArtifactParameters {
|
||||
Type: string;
|
||||
Name: string;
|
||||
RetentionDays?: number;
|
||||
}
|
||||
export interface PatchArtifactSize {
|
||||
Size: number;
|
||||
}
|
||||
export interface PatchArtifactSizeSuccessResponse {
|
||||
containerId: number;
|
||||
size: number;
|
||||
signedContent: string;
|
||||
type: string;
|
||||
name: string;
|
||||
url: string;
|
||||
uploadUrl: string;
|
||||
}
|
||||
export interface UploadResults {
|
||||
uploadSize: number;
|
||||
totalSize: number;
|
||||
failedItems: string[];
|
||||
}
|
||||
export interface ListArtifactsResponse {
|
||||
count: number;
|
||||
value: ArtifactResponse[];
|
||||
}
|
||||
export interface QueryArtifactResponse {
|
||||
count: number;
|
||||
value: ContainerEntry[];
|
||||
}
|
||||
export interface ContainerEntry {
|
||||
containerId: number;
|
||||
scopeIdentifier: string;
|
||||
path: string;
|
||||
itemType: string;
|
||||
status: string;
|
||||
fileLength?: number;
|
||||
fileEncoding?: number;
|
||||
fileType?: number;
|
||||
dateCreated: string;
|
||||
dateLastModified: string;
|
||||
createdBy: string;
|
||||
lastModifiedBy: string;
|
||||
itemLocation: string;
|
||||
contentLocation: string;
|
||||
fileId?: number;
|
||||
contentId: string;
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
//# sourceMappingURL=contracts.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"contracts.js","sourceRoot":"","sources":["../../src/internal/contracts.ts"],"names":[],"mappings":""}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/// <reference types="node" />
|
||||
import * as fs from 'fs';
|
||||
import { ListArtifactsResponse, QueryArtifactResponse } from './contracts';
|
||||
import { IHttpClientResponse } from '@actions/http-client/interfaces';
|
||||
import { DownloadItem } from './download-specification';
|
||||
export declare class DownloadHttpClient {
|
||||
private downloadHttpManager;
|
||||
private statusReporter;
|
||||
constructor();
|
||||
/**
|
||||
* Gets a list of all artifacts that are in a specific container
|
||||
*/
|
||||
listArtifacts(): Promise<ListArtifactsResponse>;
|
||||
/**
|
||||
* Fetches a set of container items that describe the contents of an artifact
|
||||
* @param artifactName the name of the artifact
|
||||
* @param containerUrl the artifact container URL for the run
|
||||
*/
|
||||
getContainerItems(artifactName: string, containerUrl: string): Promise<QueryArtifactResponse>;
|
||||
/**
|
||||
* Concurrently downloads all the files that are part of an artifact
|
||||
* @param downloadItems information about what items to download and where to save them
|
||||
*/
|
||||
downloadSingleArtifact(downloadItems: DownloadItem[]): Promise<void>;
|
||||
/**
|
||||
* Downloads an individual file
|
||||
* @param httpClientIndex the index of the http client that is used to make all of the calls
|
||||
* @param artifactLocation origin location where a file will be downloaded from
|
||||
* @param downloadPath destination location for the file being downloaded
|
||||
*/
|
||||
private downloadIndividualFile;
|
||||
/**
|
||||
* Pipes the response from downloading an individual file to the appropriate destination stream while decoding gzip content if necessary
|
||||
* @param response the http response received when downloading a file
|
||||
* @param destinationStream the stream where the file should be written to
|
||||
* @param isGzip a boolean denoting if the content is compressed using gzip and if we need to decode it
|
||||
*/
|
||||
pipeResponseToFile(response: IHttpClientResponse, destinationStream: fs.WriteStream, isGzip: boolean): Promise<void>;
|
||||
}
|
||||
+275
@@ -0,0 +1,275 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
||||
result["default"] = mod;
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const fs = __importStar(require("fs"));
|
||||
const core = __importStar(require("@actions/core"));
|
||||
const zlib = __importStar(require("zlib"));
|
||||
const utils_1 = require("./utils");
|
||||
const url_1 = require("url");
|
||||
const status_reporter_1 = require("./status-reporter");
|
||||
const perf_hooks_1 = require("perf_hooks");
|
||||
const http_manager_1 = require("./http-manager");
|
||||
const config_variables_1 = require("./config-variables");
|
||||
const requestUtils_1 = require("./requestUtils");
|
||||
class DownloadHttpClient {
|
||||
constructor() {
|
||||
this.downloadHttpManager = new http_manager_1.HttpManager(config_variables_1.getDownloadFileConcurrency(), '@actions/artifact-download');
|
||||
// downloads are usually significantly faster than uploads so display status information every second
|
||||
this.statusReporter = new status_reporter_1.StatusReporter(1000);
|
||||
}
|
||||
/**
|
||||
* Gets a list of all artifacts that are in a specific container
|
||||
*/
|
||||
listArtifacts() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const artifactUrl = utils_1.getArtifactUrl();
|
||||
// use the first client from the httpManager, `keep-alive` is not used so the connection will close immediately
|
||||
const client = this.downloadHttpManager.getClient(0);
|
||||
const headers = utils_1.getDownloadHeaders('application/json');
|
||||
const response = yield requestUtils_1.retryHttpClientRequest('List Artifacts', () => __awaiter(this, void 0, void 0, function* () { return client.get(artifactUrl, headers); }));
|
||||
const body = yield response.readBody();
|
||||
return JSON.parse(body);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Fetches a set of container items that describe the contents of an artifact
|
||||
* @param artifactName the name of the artifact
|
||||
* @param containerUrl the artifact container URL for the run
|
||||
*/
|
||||
getContainerItems(artifactName, containerUrl) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
// the itemPath search parameter controls which containers will be returned
|
||||
const resourceUrl = new url_1.URL(containerUrl);
|
||||
resourceUrl.searchParams.append('itemPath', artifactName);
|
||||
// use the first client from the httpManager, `keep-alive` is not used so the connection will close immediately
|
||||
const client = this.downloadHttpManager.getClient(0);
|
||||
const headers = utils_1.getDownloadHeaders('application/json');
|
||||
const response = yield requestUtils_1.retryHttpClientRequest('Get Container Items', () => __awaiter(this, void 0, void 0, function* () { return client.get(resourceUrl.toString(), headers); }));
|
||||
const body = yield response.readBody();
|
||||
return JSON.parse(body);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Concurrently downloads all the files that are part of an artifact
|
||||
* @param downloadItems information about what items to download and where to save them
|
||||
*/
|
||||
downloadSingleArtifact(downloadItems) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const DOWNLOAD_CONCURRENCY = config_variables_1.getDownloadFileConcurrency();
|
||||
// limit the number of files downloaded at a single time
|
||||
core.debug(`Download file concurrency is set to ${DOWNLOAD_CONCURRENCY}`);
|
||||
const parallelDownloads = [...new Array(DOWNLOAD_CONCURRENCY).keys()];
|
||||
let currentFile = 0;
|
||||
let downloadedFiles = 0;
|
||||
core.info(`Total number of files that will be downloaded: ${downloadItems.length}`);
|
||||
this.statusReporter.setTotalNumberOfFilesToProcess(downloadItems.length);
|
||||
this.statusReporter.start();
|
||||
yield Promise.all(parallelDownloads.map((index) => __awaiter(this, void 0, void 0, function* () {
|
||||
while (currentFile < downloadItems.length) {
|
||||
const currentFileToDownload = downloadItems[currentFile];
|
||||
currentFile += 1;
|
||||
const startTime = perf_hooks_1.performance.now();
|
||||
yield this.downloadIndividualFile(index, currentFileToDownload.sourceLocation, currentFileToDownload.targetPath);
|
||||
if (core.isDebug()) {
|
||||
core.debug(`File: ${++downloadedFiles}/${downloadItems.length}. ${currentFileToDownload.targetPath} took ${(perf_hooks_1.performance.now() - startTime).toFixed(3)} milliseconds to finish downloading`);
|
||||
}
|
||||
this.statusReporter.incrementProcessedCount();
|
||||
}
|
||||
})))
|
||||
.catch(error => {
|
||||
throw new Error(`Unable to download the artifact: ${error}`);
|
||||
})
|
||||
.finally(() => {
|
||||
this.statusReporter.stop();
|
||||
// safety dispose all connections
|
||||
this.downloadHttpManager.disposeAndReplaceAllClients();
|
||||
});
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Downloads an individual file
|
||||
* @param httpClientIndex the index of the http client that is used to make all of the calls
|
||||
* @param artifactLocation origin location where a file will be downloaded from
|
||||
* @param downloadPath destination location for the file being downloaded
|
||||
*/
|
||||
downloadIndividualFile(httpClientIndex, artifactLocation, downloadPath) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
let retryCount = 0;
|
||||
const retryLimit = config_variables_1.getRetryLimit();
|
||||
let destinationStream = fs.createWriteStream(downloadPath);
|
||||
const headers = utils_1.getDownloadHeaders('application/json', true, true);
|
||||
// a single GET request is used to download a file
|
||||
const makeDownloadRequest = () => __awaiter(this, void 0, void 0, function* () {
|
||||
const client = this.downloadHttpManager.getClient(httpClientIndex);
|
||||
return yield client.get(artifactLocation, headers);
|
||||
});
|
||||
// check the response headers to determine if the file was compressed using gzip
|
||||
const isGzip = (incomingHeaders) => {
|
||||
return ('content-encoding' in incomingHeaders &&
|
||||
incomingHeaders['content-encoding'] === 'gzip');
|
||||
};
|
||||
// Increments the current retry count and then checks if the retry limit has been reached
|
||||
// If there have been too many retries, fail so the download stops. If there is a retryAfterValue value provided,
|
||||
// it will be used
|
||||
const backOff = (retryAfterValue) => __awaiter(this, void 0, void 0, function* () {
|
||||
retryCount++;
|
||||
if (retryCount > retryLimit) {
|
||||
return Promise.reject(new Error(`Retry limit has been reached. Unable to download ${artifactLocation}`));
|
||||
}
|
||||
else {
|
||||
this.downloadHttpManager.disposeAndReplaceClient(httpClientIndex);
|
||||
if (retryAfterValue) {
|
||||
// Back off by waiting the specified time denoted by the retry-after header
|
||||
core.info(`Backoff due to too many requests, retry #${retryCount}. Waiting for ${retryAfterValue} milliseconds before continuing the download`);
|
||||
yield utils_1.sleep(retryAfterValue);
|
||||
}
|
||||
else {
|
||||
// Back off using an exponential value that depends on the retry count
|
||||
const backoffTime = utils_1.getExponentialRetryTimeInMilliseconds(retryCount);
|
||||
core.info(`Exponential backoff for retry #${retryCount}. Waiting for ${backoffTime} milliseconds before continuing the download`);
|
||||
yield utils_1.sleep(backoffTime);
|
||||
}
|
||||
core.info(`Finished backoff for retry #${retryCount}, continuing with download`);
|
||||
}
|
||||
});
|
||||
const isAllBytesReceived = (expected, received) => {
|
||||
// be lenient, if any input is missing, assume success, i.e. not truncated
|
||||
if (!expected ||
|
||||
!received ||
|
||||
process.env['ACTIONS_ARTIFACT_SKIP_DOWNLOAD_VALIDATION']) {
|
||||
core.info('Skipping download validation.');
|
||||
return true;
|
||||
}
|
||||
return parseInt(expected) === received;
|
||||
};
|
||||
const resetDestinationStream = (fileDownloadPath) => __awaiter(this, void 0, void 0, function* () {
|
||||
destinationStream.close();
|
||||
yield utils_1.rmFile(fileDownloadPath);
|
||||
destinationStream = fs.createWriteStream(fileDownloadPath);
|
||||
});
|
||||
// keep trying to download a file until a retry limit has been reached
|
||||
while (retryCount <= retryLimit) {
|
||||
let response;
|
||||
try {
|
||||
response = yield makeDownloadRequest();
|
||||
if (core.isDebug()) {
|
||||
utils_1.displayHttpDiagnostics(response);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
// if an error is caught, it is usually indicative of a timeout so retry the download
|
||||
core.info('An error occurred while attempting to download a file');
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
// increment the retryCount and use exponential backoff to wait before making the next request
|
||||
yield backOff();
|
||||
continue;
|
||||
}
|
||||
let forceRetry = false;
|
||||
if (utils_1.isSuccessStatusCode(response.message.statusCode)) {
|
||||
// The body contains the contents of the file however calling response.readBody() causes all the content to be converted to a string
|
||||
// which can cause some gzip encoded data to be lost
|
||||
// Instead of using response.readBody(), response.message is a readableStream that can be directly used to get the raw body contents
|
||||
try {
|
||||
const isGzipped = isGzip(response.message.headers);
|
||||
yield this.pipeResponseToFile(response, destinationStream, isGzipped);
|
||||
if (isGzipped ||
|
||||
isAllBytesReceived(response.message.headers['content-length'], yield utils_1.getFileSize(downloadPath))) {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
forceRetry = true;
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
// retry on error, most likely streams were corrupted
|
||||
forceRetry = true;
|
||||
}
|
||||
}
|
||||
if (forceRetry || utils_1.isRetryableStatusCode(response.message.statusCode)) {
|
||||
core.info(`A ${response.message.statusCode} response code has been received while attempting to download an artifact`);
|
||||
resetDestinationStream(downloadPath);
|
||||
// if a throttled status code is received, try to get the retryAfter header value, else differ to standard exponential backoff
|
||||
utils_1.isThrottledStatusCode(response.message.statusCode)
|
||||
? yield backOff(utils_1.tryGetRetryAfterValueTimeInMilliseconds(response.message.headers))
|
||||
: yield backOff();
|
||||
}
|
||||
else {
|
||||
// Some unexpected response code, fail immediately and stop the download
|
||||
utils_1.displayHttpDiagnostics(response);
|
||||
return Promise.reject(new Error(`Unexpected http ${response.message.statusCode} during download for ${artifactLocation}`));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Pipes the response from downloading an individual file to the appropriate destination stream while decoding gzip content if necessary
|
||||
* @param response the http response received when downloading a file
|
||||
* @param destinationStream the stream where the file should be written to
|
||||
* @param isGzip a boolean denoting if the content is compressed using gzip and if we need to decode it
|
||||
*/
|
||||
pipeResponseToFile(response, destinationStream, isGzip) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
yield new Promise((resolve, reject) => {
|
||||
if (isGzip) {
|
||||
const gunzip = zlib.createGunzip();
|
||||
response.message
|
||||
.on('error', error => {
|
||||
core.error(`An error occurred while attempting to read the response stream`);
|
||||
gunzip.close();
|
||||
destinationStream.close();
|
||||
reject(error);
|
||||
})
|
||||
.pipe(gunzip)
|
||||
.on('error', error => {
|
||||
core.error(`An error occurred while attempting to decompress the response stream`);
|
||||
destinationStream.close();
|
||||
reject(error);
|
||||
})
|
||||
.pipe(destinationStream)
|
||||
.on('close', () => {
|
||||
resolve();
|
||||
})
|
||||
.on('error', error => {
|
||||
core.error(`An error occurred while writing a downloaded file to ${destinationStream.path}`);
|
||||
reject(error);
|
||||
});
|
||||
}
|
||||
else {
|
||||
response.message
|
||||
.on('error', error => {
|
||||
core.error(`An error occurred while attempting to read the response stream`);
|
||||
destinationStream.close();
|
||||
reject(error);
|
||||
})
|
||||
.pipe(destinationStream)
|
||||
.on('close', () => {
|
||||
resolve();
|
||||
})
|
||||
.on('error', error => {
|
||||
core.error(`An error occurred while writing a downloaded file to ${destinationStream.path}`);
|
||||
reject(error);
|
||||
});
|
||||
}
|
||||
});
|
||||
return;
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.DownloadHttpClient = DownloadHttpClient;
|
||||
//# sourceMappingURL=download-http-client.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
+7
@@ -0,0 +1,7 @@
|
||||
export interface DownloadOptions {
|
||||
/**
|
||||
* Specifies if a folder is created for the artifact that is downloaded (contents downloaded into this folder),
|
||||
* defaults to false if not specified
|
||||
* */
|
||||
createArtifactFolder?: boolean;
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
//# sourceMappingURL=download-options.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"download-options.js","sourceRoot":"","sources":["../../src/internal/download-options.ts"],"names":[],"mappings":""}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
export interface DownloadResponse {
|
||||
/**
|
||||
* The name of the artifact that was downloaded
|
||||
*/
|
||||
artifactName: string;
|
||||
/**
|
||||
* The full Path to where the artifact was downloaded
|
||||
*/
|
||||
downloadPath: string;
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
//# sourceMappingURL=download-response.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"download-response.js","sourceRoot":"","sources":["../../src/internal/download-response.ts"],"names":[],"mappings":""}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import { ContainerEntry } from './contracts';
|
||||
export interface DownloadSpecification {
|
||||
rootDownloadLocation: string;
|
||||
directoryStructure: string[];
|
||||
emptyFilesToCreate: string[];
|
||||
filesToDownload: DownloadItem[];
|
||||
}
|
||||
export interface DownloadItem {
|
||||
sourceLocation: string;
|
||||
targetPath: string;
|
||||
}
|
||||
/**
|
||||
* Creates a specification for a set of files that will be downloaded
|
||||
* @param artifactName the name of the artifact
|
||||
* @param artifactEntries a set of container entries that describe that files that make up an artifact
|
||||
* @param downloadPath the path where the artifact will be downloaded to
|
||||
* @param includeRootDirectory specifies if there should be an extra directory (denoted by the artifact name) where the artifact files should be downloaded to
|
||||
*/
|
||||
export declare function getDownloadSpecification(artifactName: string, artifactEntries: ContainerEntry[], downloadPath: string, includeRootDirectory: boolean): DownloadSpecification;
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
"use strict";
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
||||
result["default"] = mod;
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const path = __importStar(require("path"));
|
||||
/**
|
||||
* Creates a specification for a set of files that will be downloaded
|
||||
* @param artifactName the name of the artifact
|
||||
* @param artifactEntries a set of container entries that describe that files that make up an artifact
|
||||
* @param downloadPath the path where the artifact will be downloaded to
|
||||
* @param includeRootDirectory specifies if there should be an extra directory (denoted by the artifact name) where the artifact files should be downloaded to
|
||||
*/
|
||||
function getDownloadSpecification(artifactName, artifactEntries, downloadPath, includeRootDirectory) {
|
||||
// use a set for the directory paths so that there are no duplicates
|
||||
const directories = new Set();
|
||||
const specifications = {
|
||||
rootDownloadLocation: includeRootDirectory
|
||||
? path.join(downloadPath, artifactName)
|
||||
: downloadPath,
|
||||
directoryStructure: [],
|
||||
emptyFilesToCreate: [],
|
||||
filesToDownload: []
|
||||
};
|
||||
for (const entry of artifactEntries) {
|
||||
// Ignore artifacts in the container that don't begin with the same name
|
||||
if (entry.path.startsWith(`${artifactName}/`) ||
|
||||
entry.path.startsWith(`${artifactName}\\`)) {
|
||||
// normalize all separators to the local OS
|
||||
const normalizedPathEntry = path.normalize(entry.path);
|
||||
// entry.path always starts with the artifact name, if includeRootDirectory is false, remove the name from the beginning of the path
|
||||
const filePath = path.join(downloadPath, includeRootDirectory
|
||||
? normalizedPathEntry
|
||||
: normalizedPathEntry.replace(artifactName, ''));
|
||||
// Case insensitive folder structure maintained in the backend, not every folder is created so the 'folder'
|
||||
// itemType cannot be relied upon. The file must be used to determine the directory structure
|
||||
if (entry.itemType === 'file') {
|
||||
// Get the directories that we need to create from the filePath for each individual file
|
||||
directories.add(path.dirname(filePath));
|
||||
if (entry.fileLength === 0) {
|
||||
// An empty file was uploaded, create the empty files locally so that no extra http calls are made
|
||||
specifications.emptyFilesToCreate.push(filePath);
|
||||
}
|
||||
else {
|
||||
specifications.filesToDownload.push({
|
||||
sourceLocation: entry.contentLocation,
|
||||
targetPath: filePath
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
specifications.directoryStructure = Array.from(directories);
|
||||
return specifications;
|
||||
}
|
||||
exports.getDownloadSpecification = getDownloadSpecification;
|
||||
//# sourceMappingURL=download-specification.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"download-specification.js","sourceRoot":"","sources":["../../src/internal/download-specification.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAA4B;AAyB5B;;;;;;GAMG;AACH,SAAgB,wBAAwB,CACtC,YAAoB,EACpB,eAAiC,EACjC,YAAoB,EACpB,oBAA6B;IAE7B,oEAAoE;IACpE,MAAM,WAAW,GAAG,IAAI,GAAG,EAAU,CAAA;IAErC,MAAM,cAAc,GAA0B;QAC5C,oBAAoB,EAAE,oBAAoB;YACxC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,YAAY,CAAC;YACvC,CAAC,CAAC,YAAY;QAChB,kBAAkB,EAAE,EAAE;QACtB,kBAAkB,EAAE,EAAE;QACtB,eAAe,EAAE,EAAE;KACpB,CAAA;IAED,KAAK,MAAM,KAAK,IAAI,eAAe,EAAE;QACnC,wEAAwE;QACxE,IACE,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,YAAY,GAAG,CAAC;YACzC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,YAAY,IAAI,CAAC,EAC1C;YACA,2CAA2C;YAC3C,MAAM,mBAAmB,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;YACtD,oIAAoI;YACpI,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CACxB,YAAY,EACZ,oBAAoB;gBAClB,CAAC,CAAC,mBAAmB;gBACrB,CAAC,CAAC,mBAAmB,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAClD,CAAA;YAED,2GAA2G;YAC3G,6FAA6F;YAC7F,IAAI,KAAK,CAAC,QAAQ,KAAK,MAAM,EAAE;gBAC7B,wFAAwF;gBACxF,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAA;gBACvC,IAAI,KAAK,CAAC,UAAU,KAAK,CAAC,EAAE;oBAC1B,kGAAkG;oBAClG,cAAc,CAAC,kBAAkB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;iBACjD;qBAAM;oBACL,cAAc,CAAC,eAAe,CAAC,IAAI,CAAC;wBAClC,cAAc,EAAE,KAAK,CAAC,eAAe;wBACrC,UAAU,EAAE,QAAQ;qBACrB,CAAC,CAAA;iBACH;aACF;SACF;KACF;IAED,cAAc,CAAC,kBAAkB,GAAG,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;IAC3D,OAAO,cAAc,CAAA;AACvB,CAAC;AAtDD,4DAsDC"}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import { HttpClient } from '@actions/http-client/index';
|
||||
/**
|
||||
* Used for managing http clients during either upload or download
|
||||
*/
|
||||
export declare class HttpManager {
|
||||
private clients;
|
||||
private userAgent;
|
||||
constructor(clientCount: number, userAgent: string);
|
||||
getClient(index: number): HttpClient;
|
||||
disposeAndReplaceClient(index: number): void;
|
||||
disposeAndReplaceAllClients(): void;
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const utils_1 = require("./utils");
|
||||
/**
|
||||
* Used for managing http clients during either upload or download
|
||||
*/
|
||||
class HttpManager {
|
||||
constructor(clientCount, userAgent) {
|
||||
if (clientCount < 1) {
|
||||
throw new Error('There must be at least one client');
|
||||
}
|
||||
this.userAgent = userAgent;
|
||||
this.clients = new Array(clientCount).fill(utils_1.createHttpClient(userAgent));
|
||||
}
|
||||
getClient(index) {
|
||||
return this.clients[index];
|
||||
}
|
||||
// client disposal is necessary if a keep-alive connection is used to properly close the connection
|
||||
// for more information see: https://github.com/actions/http-client/blob/04e5ad73cd3fd1f5610a32116b0759eddf6570d2/index.ts#L292
|
||||
disposeAndReplaceClient(index) {
|
||||
this.clients[index].dispose();
|
||||
this.clients[index] = utils_1.createHttpClient(this.userAgent);
|
||||
}
|
||||
disposeAndReplaceAllClients() {
|
||||
for (const [index] of this.clients.entries()) {
|
||||
this.disposeAndReplaceClient(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.HttpManager = HttpManager;
|
||||
//# sourceMappingURL=http-manager.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"http-manager.js","sourceRoot":"","sources":["../../src/internal/http-manager.ts"],"names":[],"mappings":";;AACA,mCAAwC;AAExC;;GAEG;AACH,MAAa,WAAW;IAItB,YAAY,WAAmB,EAAE,SAAiB;QAChD,IAAI,WAAW,GAAG,CAAC,EAAE;YACnB,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAA;SACrD;QACD,IAAI,CAAC,SAAS,GAAG,SAAS,CAAA;QAC1B,IAAI,CAAC,OAAO,GAAG,IAAI,KAAK,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,wBAAgB,CAAC,SAAS,CAAC,CAAC,CAAA;IACzE,CAAC;IAED,SAAS,CAAC,KAAa;QACrB,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;IAC5B,CAAC;IAED,mGAAmG;IACnG,+HAA+H;IAC/H,uBAAuB,CAAC,KAAa;QACnC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAA;QAC7B,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,wBAAgB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;IACxD,CAAC;IAED,2BAA2B;QACzB,KAAK,MAAM,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE;YAC5C,IAAI,CAAC,uBAAuB,CAAC,KAAK,CAAC,CAAA;SACpC;IACH,CAAC;CACF;AA5BD,kCA4BC"}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
import { IHttpClientResponse } from '@actions/http-client/interfaces';
|
||||
export declare function retry(name: string, operation: () => Promise<IHttpClientResponse>, customErrorMessages: Map<number, string>, maxAttempts: number): Promise<IHttpClientResponse>;
|
||||
export declare function retryHttpClientRequest<T>(name: string, method: () => Promise<IHttpClientResponse>, customErrorMessages?: Map<number, string>, maxAttempts?: number): Promise<IHttpClientResponse>;
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
||||
result["default"] = mod;
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const utils_1 = require("./utils");
|
||||
const core = __importStar(require("@actions/core"));
|
||||
const config_variables_1 = require("./config-variables");
|
||||
function retry(name, operation, customErrorMessages, maxAttempts) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
let response = undefined;
|
||||
let statusCode = undefined;
|
||||
let isRetryable = false;
|
||||
let errorMessage = '';
|
||||
let customErrorInformation = undefined;
|
||||
let attempt = 1;
|
||||
while (attempt <= maxAttempts) {
|
||||
try {
|
||||
response = yield operation();
|
||||
statusCode = response.message.statusCode;
|
||||
if (utils_1.isSuccessStatusCode(statusCode)) {
|
||||
return response;
|
||||
}
|
||||
// Extra error information that we want to display if a particular response code is hit
|
||||
if (statusCode) {
|
||||
customErrorInformation = customErrorMessages.get(statusCode);
|
||||
}
|
||||
isRetryable = utils_1.isRetryableStatusCode(statusCode);
|
||||
errorMessage = `Artifact service responded with ${statusCode}`;
|
||||
}
|
||||
catch (error) {
|
||||
isRetryable = true;
|
||||
errorMessage = error.message;
|
||||
}
|
||||
if (!isRetryable) {
|
||||
core.info(`${name} - Error is not retryable`);
|
||||
if (response) {
|
||||
utils_1.displayHttpDiagnostics(response);
|
||||
}
|
||||
break;
|
||||
}
|
||||
core.info(`${name} - Attempt ${attempt} of ${maxAttempts} failed with error: ${errorMessage}`);
|
||||
yield utils_1.sleep(utils_1.getExponentialRetryTimeInMilliseconds(attempt));
|
||||
attempt++;
|
||||
}
|
||||
if (response) {
|
||||
utils_1.displayHttpDiagnostics(response);
|
||||
}
|
||||
if (customErrorInformation) {
|
||||
throw Error(`${name} failed: ${customErrorInformation}`);
|
||||
}
|
||||
throw Error(`${name} failed: ${errorMessage}`);
|
||||
});
|
||||
}
|
||||
exports.retry = retry;
|
||||
function retryHttpClientRequest(name, method, customErrorMessages = new Map(), maxAttempts = config_variables_1.getRetryLimit()) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
return yield retry(name, method, customErrorMessages, maxAttempts);
|
||||
});
|
||||
}
|
||||
exports.retryHttpClientRequest = retryHttpClientRequest;
|
||||
//# sourceMappingURL=requestUtils.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"requestUtils.js","sourceRoot":"","sources":["../../src/internal/requestUtils.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AACA,mCAMgB;AAChB,oDAAqC;AACrC,yDAAgD;AAEhD,SAAsB,KAAK,CACzB,IAAY,EACZ,SAA6C,EAC7C,mBAAwC,EACxC,WAAmB;;QAEnB,IAAI,QAAQ,GAAoC,SAAS,CAAA;QACzD,IAAI,UAAU,GAAuB,SAAS,CAAA;QAC9C,IAAI,WAAW,GAAG,KAAK,CAAA;QACvB,IAAI,YAAY,GAAG,EAAE,CAAA;QACrB,IAAI,sBAAsB,GAAuB,SAAS,CAAA;QAC1D,IAAI,OAAO,GAAG,CAAC,CAAA;QAEf,OAAO,OAAO,IAAI,WAAW,EAAE;YAC7B,IAAI;gBACF,QAAQ,GAAG,MAAM,SAAS,EAAE,CAAA;gBAC5B,UAAU,GAAG,QAAQ,CAAC,OAAO,CAAC,UAAU,CAAA;gBAExC,IAAI,2BAAmB,CAAC,UAAU,CAAC,EAAE;oBACnC,OAAO,QAAQ,CAAA;iBAChB;gBAED,uFAAuF;gBACvF,IAAI,UAAU,EAAE;oBACd,sBAAsB,GAAG,mBAAmB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAA;iBAC7D;gBAED,WAAW,GAAG,6BAAqB,CAAC,UAAU,CAAC,CAAA;gBAC/C,YAAY,GAAG,mCAAmC,UAAU,EAAE,CAAA;aAC/D;YAAC,OAAO,KAAK,EAAE;gBACd,WAAW,GAAG,IAAI,CAAA;gBAClB,YAAY,GAAG,KAAK,CAAC,OAAO,CAAA;aAC7B;YAED,IAAI,CAAC,WAAW,EAAE;gBAChB,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,2BAA2B,CAAC,CAAA;gBAC7C,IAAI,QAAQ,EAAE;oBACZ,8BAAsB,CAAC,QAAQ,CAAC,CAAA;iBACjC;gBACD,MAAK;aACN;YAED,IAAI,CAAC,IAAI,CACP,GAAG,IAAI,cAAc,OAAO,OAAO,WAAW,uBAAuB,YAAY,EAAE,CACpF,CAAA;YAED,MAAM,aAAK,CAAC,6CAAqC,CAAC,OAAO,CAAC,CAAC,CAAA;YAC3D,OAAO,EAAE,CAAA;SACV;QAED,IAAI,QAAQ,EAAE;YACZ,8BAAsB,CAAC,QAAQ,CAAC,CAAA;SACjC;QAED,IAAI,sBAAsB,EAAE;YAC1B,MAAM,KAAK,CAAC,GAAG,IAAI,YAAY,sBAAsB,EAAE,CAAC,CAAA;SACzD;QACD,MAAM,KAAK,CAAC,GAAG,IAAI,YAAY,YAAY,EAAE,CAAC,CAAA;IAChD,CAAC;CAAA;AA1DD,sBA0DC;AAED,SAAsB,sBAAsB,CAC1C,IAAY,EACZ,MAA0C,EAC1C,sBAA2C,IAAI,GAAG,EAAE,EACpD,WAAW,GAAG,gCAAa,EAAE;;QAE7B,OAAO,MAAM,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,mBAAmB,EAAE,WAAW,CAAC,CAAA;IACpE,CAAC;CAAA;AAPD,wDAOC"}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Status Reporter that displays information about the progress/status of an artifact that is being uploaded or downloaded
|
||||
*
|
||||
* Variable display time that can be adjusted using the displayFrequencyInMilliseconds variable
|
||||
* The total status of the upload/download gets displayed according to this value
|
||||
* If there is a large file that is being uploaded, extra information about the individual status can also be displayed using the updateLargeFileStatus function
|
||||
*/
|
||||
export declare class StatusReporter {
|
||||
private totalNumberOfFilesToProcess;
|
||||
private processedCount;
|
||||
private displayFrequencyInMilliseconds;
|
||||
private largeFiles;
|
||||
private totalFileStatus;
|
||||
private largeFileStatus;
|
||||
constructor(displayFrequencyInMilliseconds: number);
|
||||
setTotalNumberOfFilesToProcess(fileTotal: number): void;
|
||||
start(): void;
|
||||
updateLargeFileStatus(fileName: string, numerator: number, denominator: number): void;
|
||||
stop(): void;
|
||||
incrementProcessedCount(): void;
|
||||
private formatPercentage;
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const core_1 = require("@actions/core");
|
||||
/**
|
||||
* Status Reporter that displays information about the progress/status of an artifact that is being uploaded or downloaded
|
||||
*
|
||||
* Variable display time that can be adjusted using the displayFrequencyInMilliseconds variable
|
||||
* The total status of the upload/download gets displayed according to this value
|
||||
* If there is a large file that is being uploaded, extra information about the individual status can also be displayed using the updateLargeFileStatus function
|
||||
*/
|
||||
class StatusReporter {
|
||||
constructor(displayFrequencyInMilliseconds) {
|
||||
this.totalNumberOfFilesToProcess = 0;
|
||||
this.processedCount = 0;
|
||||
this.largeFiles = new Map();
|
||||
this.totalFileStatus = undefined;
|
||||
this.largeFileStatus = undefined;
|
||||
this.displayFrequencyInMilliseconds = displayFrequencyInMilliseconds;
|
||||
}
|
||||
setTotalNumberOfFilesToProcess(fileTotal) {
|
||||
this.totalNumberOfFilesToProcess = fileTotal;
|
||||
}
|
||||
start() {
|
||||
// displays information about the total upload/download status
|
||||
this.totalFileStatus = setInterval(() => {
|
||||
// display 1 decimal place without any rounding
|
||||
const percentage = this.formatPercentage(this.processedCount, this.totalNumberOfFilesToProcess);
|
||||
core_1.info(`Total file count: ${this.totalNumberOfFilesToProcess} ---- Processed file #${this.processedCount} (${percentage.slice(0, percentage.indexOf('.') + 2)}%)`);
|
||||
}, this.displayFrequencyInMilliseconds);
|
||||
// displays extra information about any large files that take a significant amount of time to upload or download every 1 second
|
||||
this.largeFileStatus = setInterval(() => {
|
||||
for (const value of Array.from(this.largeFiles.values())) {
|
||||
core_1.info(value);
|
||||
}
|
||||
// delete all entries in the map after displaying the information so it will not be displayed again unless explicitly added
|
||||
this.largeFiles.clear();
|
||||
}, 1000);
|
||||
}
|
||||
// if there is a large file that is being uploaded in chunks, this is used to display extra information about the status of the upload
|
||||
updateLargeFileStatus(fileName, numerator, denominator) {
|
||||
// display 1 decimal place without any rounding
|
||||
const percentage = this.formatPercentage(numerator, denominator);
|
||||
const displayInformation = `Uploading ${fileName} (${percentage.slice(0, percentage.indexOf('.') + 2)}%)`;
|
||||
// any previously added display information should be overwritten for the specific large file because a map is being used
|
||||
this.largeFiles.set(fileName, displayInformation);
|
||||
}
|
||||
stop() {
|
||||
if (this.totalFileStatus) {
|
||||
clearInterval(this.totalFileStatus);
|
||||
}
|
||||
if (this.largeFileStatus) {
|
||||
clearInterval(this.largeFileStatus);
|
||||
}
|
||||
}
|
||||
incrementProcessedCount() {
|
||||
this.processedCount++;
|
||||
}
|
||||
formatPercentage(numerator, denominator) {
|
||||
// toFixed() rounds, so use extra precision to display accurate information even though 4 decimal places are not displayed
|
||||
return ((numerator / denominator) * 100).toFixed(4).toString();
|
||||
}
|
||||
}
|
||||
exports.StatusReporter = StatusReporter;
|
||||
//# sourceMappingURL=status-reporter.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"status-reporter.js","sourceRoot":"","sources":["../../src/internal/status-reporter.ts"],"names":[],"mappings":";;AAAA,wCAAkC;AAElC;;;;;;GAMG;AAEH,MAAa,cAAc;IAQzB,YAAY,8BAAsC;QAP1C,gCAA2B,GAAG,CAAC,CAAA;QAC/B,mBAAc,GAAG,CAAC,CAAA;QAElB,eAAU,GAAG,IAAI,GAAG,EAAkB,CAAA;QAK5C,IAAI,CAAC,eAAe,GAAG,SAAS,CAAA;QAChC,IAAI,CAAC,eAAe,GAAG,SAAS,CAAA;QAChC,IAAI,CAAC,8BAA8B,GAAG,8BAA8B,CAAA;IACtE,CAAC;IAED,8BAA8B,CAAC,SAAiB;QAC9C,IAAI,CAAC,2BAA2B,GAAG,SAAS,CAAA;IAC9C,CAAC;IAED,KAAK;QACH,8DAA8D;QAC9D,IAAI,CAAC,eAAe,GAAG,WAAW,CAAC,GAAG,EAAE;YACtC,+CAA+C;YAC/C,MAAM,UAAU,GAAG,IAAI,CAAC,gBAAgB,CACtC,IAAI,CAAC,cAAc,EACnB,IAAI,CAAC,2BAA2B,CACjC,CAAA;YACD,WAAI,CACF,qBACE,IAAI,CAAC,2BACP,yBAAyB,IAAI,CAAC,cAAc,KAAK,UAAU,CAAC,KAAK,CAC/D,CAAC,EACD,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAC5B,IAAI,CACN,CAAA;QACH,CAAC,EAAE,IAAI,CAAC,8BAA8B,CAAC,CAAA;QAEvC,+HAA+H;QAC/H,IAAI,CAAC,eAAe,GAAG,WAAW,CAAC,GAAG,EAAE;YACtC,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE;gBACxD,WAAI,CAAC,KAAK,CAAC,CAAA;aACZ;YACD,2HAA2H;YAC3H,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAA;QACzB,CAAC,EAAE,IAAI,CAAC,CAAA;IACV,CAAC;IAED,sIAAsI;IACtI,qBAAqB,CACnB,QAAgB,EAChB,SAAiB,EACjB,WAAmB;QAEnB,+CAA+C;QAC/C,MAAM,UAAU,GAAG,IAAI,CAAC,gBAAgB,CAAC,SAAS,EAAE,WAAW,CAAC,CAAA;QAChE,MAAM,kBAAkB,GAAG,aAAa,QAAQ,KAAK,UAAU,CAAC,KAAK,CACnE,CAAC,EACD,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAC5B,IAAI,CAAA;QAEL,yHAAyH;QACzH,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAA;IACnD,CAAC;IAED,IAAI;QACF,IAAI,IAAI,CAAC,eAAe,EAAE;YACxB,aAAa,CAAC,IAAI,CAAC,eAAe,CAAC,CAAA;SACpC;QAED,IAAI,IAAI,CAAC,eAAe,EAAE;YACxB,aAAa,CAAC,IAAI,CAAC,eAAe,CAAC,CAAA;SACpC;IACH,CAAC;IAED,uBAAuB;QACrB,IAAI,CAAC,cAAc,EAAE,CAAA;IACvB,CAAC;IAEO,gBAAgB,CAAC,SAAiB,EAAE,WAAmB;QAC7D,0HAA0H;QAC1H,OAAO,CAAC,CAAC,SAAS,GAAG,WAAW,CAAC,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAA;IAChE,CAAC;CACF;AAjFD,wCAiFC"}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/// <reference types="node" />
|
||||
/**
|
||||
* Creates a Gzip compressed file of an original file at the provided temporary filepath location
|
||||
* @param {string} originalFilePath filepath of whatever will be compressed. The original file will be unmodified
|
||||
* @param {string} tempFilePath the location of where the Gzip file will be created
|
||||
* @returns the size of gzip file that gets created
|
||||
*/
|
||||
export declare function createGZipFileOnDisk(originalFilePath: string, tempFilePath: string): Promise<number>;
|
||||
/**
|
||||
* Creates a GZip file in memory using a buffer. Should be used for smaller files to reduce disk I/O
|
||||
* @param originalFilePath the path to the original file that is being GZipped
|
||||
* @returns a buffer with the GZip file
|
||||
*/
|
||||
export declare function createGZipFileInBuffer(originalFilePath: string): Promise<Buffer>;
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __asyncValues = (this && this.__asyncValues) || function (o) {
|
||||
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
||||
var m = o[Symbol.asyncIterator], i;
|
||||
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
|
||||
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
|
||||
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
||||
};
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
||||
result["default"] = mod;
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const fs = __importStar(require("fs"));
|
||||
const zlib = __importStar(require("zlib"));
|
||||
const util_1 = require("util");
|
||||
const stat = util_1.promisify(fs.stat);
|
||||
/**
|
||||
* Creates a Gzip compressed file of an original file at the provided temporary filepath location
|
||||
* @param {string} originalFilePath filepath of whatever will be compressed. The original file will be unmodified
|
||||
* @param {string} tempFilePath the location of where the Gzip file will be created
|
||||
* @returns the size of gzip file that gets created
|
||||
*/
|
||||
function createGZipFileOnDisk(originalFilePath, tempFilePath) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
return new Promise((resolve, reject) => {
|
||||
const inputStream = fs.createReadStream(originalFilePath);
|
||||
const gzip = zlib.createGzip();
|
||||
const outputStream = fs.createWriteStream(tempFilePath);
|
||||
inputStream.pipe(gzip).pipe(outputStream);
|
||||
outputStream.on('finish', () => __awaiter(this, void 0, void 0, function* () {
|
||||
// wait for stream to finish before calculating the size which is needed as part of the Content-Length header when starting an upload
|
||||
const size = (yield stat(tempFilePath)).size;
|
||||
resolve(size);
|
||||
}));
|
||||
outputStream.on('error', error => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
reject;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
exports.createGZipFileOnDisk = createGZipFileOnDisk;
|
||||
/**
|
||||
* Creates a GZip file in memory using a buffer. Should be used for smaller files to reduce disk I/O
|
||||
* @param originalFilePath the path to the original file that is being GZipped
|
||||
* @returns a buffer with the GZip file
|
||||
*/
|
||||
function createGZipFileInBuffer(originalFilePath) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
return new Promise((resolve) => __awaiter(this, void 0, void 0, function* () {
|
||||
var e_1, _a;
|
||||
const inputStream = fs.createReadStream(originalFilePath);
|
||||
const gzip = zlib.createGzip();
|
||||
inputStream.pipe(gzip);
|
||||
// read stream into buffer, using experimental async iterators see https://github.com/nodejs/readable-stream/issues/403#issuecomment-479069043
|
||||
const chunks = [];
|
||||
try {
|
||||
for (var gzip_1 = __asyncValues(gzip), gzip_1_1; gzip_1_1 = yield gzip_1.next(), !gzip_1_1.done;) {
|
||||
const chunk = gzip_1_1.value;
|
||||
chunks.push(chunk);
|
||||
}
|
||||
}
|
||||
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (gzip_1_1 && !gzip_1_1.done && (_a = gzip_1.return)) yield _a.call(gzip_1);
|
||||
}
|
||||
finally { if (e_1) throw e_1.error; }
|
||||
}
|
||||
resolve(Buffer.concat(chunks));
|
||||
}));
|
||||
});
|
||||
}
|
||||
exports.createGZipFileInBuffer = createGZipFileInBuffer;
|
||||
//# sourceMappingURL=upload-gzip.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"upload-gzip.js","sourceRoot":"","sources":["../../src/internal/upload-gzip.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAAA,uCAAwB;AACxB,2CAA4B;AAC5B,+BAA8B;AAC9B,MAAM,IAAI,GAAG,gBAAS,CAAC,EAAE,CAAC,IAAI,CAAC,CAAA;AAE/B;;;;;GAKG;AACH,SAAsB,oBAAoB,CACxC,gBAAwB,EACxB,YAAoB;;QAEpB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,WAAW,GAAG,EAAE,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,CAAA;YACzD,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,EAAE,CAAA;YAC9B,MAAM,YAAY,GAAG,EAAE,CAAC,iBAAiB,CAAC,YAAY,CAAC,CAAA;YACvD,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAA;YACzC,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAS,EAAE;gBACnC,qIAAqI;gBACrI,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAA;gBAC5C,OAAO,CAAC,IAAI,CAAC,CAAA;YACf,CAAC,CAAA,CAAC,CAAA;YACF,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE;gBAC/B,sCAAsC;gBACtC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;gBAClB,MAAM,CAAA;YACR,CAAC,CAAC,CAAA;QACJ,CAAC,CAAC,CAAA;IACJ,CAAC;CAAA;AApBD,oDAoBC;AAED;;;;GAIG;AACH,SAAsB,sBAAsB,CAC1C,gBAAwB;;QAExB,OAAO,IAAI,OAAO,CAAC,CAAM,OAAO,EAAC,EAAE;;YACjC,MAAM,WAAW,GAAG,EAAE,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,CAAA;YACzD,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,EAAE,CAAA;YAC9B,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YACtB,8IAA8I;YAC9I,MAAM,MAAM,GAAG,EAAE,CAAA;;gBACjB,KAA0B,IAAA,SAAA,cAAA,IAAI,CAAA,UAAA;oBAAnB,MAAM,KAAK,iBAAA,CAAA;oBACpB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;iBACnB;;;;;;;;;YACD,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAA;QAChC,CAAC,CAAA,CAAC,CAAA;IACJ,CAAC;CAAA;AAdD,wDAcC"}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
import { ArtifactResponse, UploadResults } from './contracts';
|
||||
import { UploadSpecification } from './upload-specification';
|
||||
import { UploadOptions } from './upload-options';
|
||||
export declare class UploadHttpClient {
|
||||
private uploadHttpManager;
|
||||
private statusReporter;
|
||||
constructor();
|
||||
/**
|
||||
* Creates a file container for the new artifact in the remote blob storage/file service
|
||||
* @param {string} artifactName Name of the artifact being created
|
||||
* @returns The response from the Artifact Service if the file container was successfully created
|
||||
*/
|
||||
createArtifactInFileContainer(artifactName: string, options?: UploadOptions | undefined): Promise<ArtifactResponse>;
|
||||
/**
|
||||
* Concurrently upload all of the files in chunks
|
||||
* @param {string} uploadUrl Base Url for the artifact that was created
|
||||
* @param {SearchResult[]} filesToUpload A list of information about the files being uploaded
|
||||
* @returns The size of all the files uploaded in bytes
|
||||
*/
|
||||
uploadArtifactToFileContainer(uploadUrl: string, filesToUpload: UploadSpecification[], options?: UploadOptions): Promise<UploadResults>;
|
||||
/**
|
||||
* Asynchronously uploads a file. The file is compressed and uploaded using GZip if it is determined to save space.
|
||||
* If the upload file is bigger than the max chunk size it will be uploaded via multiple calls
|
||||
* @param {number} httpClientIndex The index of the httpClient that is being used to make all of the calls
|
||||
* @param {UploadFileParameters} parameters Information about the file that needs to be uploaded
|
||||
* @returns The size of the file that was uploaded in bytes along with any failed uploads
|
||||
*/
|
||||
private uploadFileAsync;
|
||||
/**
|
||||
* Uploads a chunk of an individual file to the specified resourceUrl. If the upload fails and the status code
|
||||
* indicates a retryable status, we try to upload the chunk as well
|
||||
* @param {number} httpClientIndex The index of the httpClient being used to make all the necessary calls
|
||||
* @param {string} resourceUrl Url of the resource that the chunk will be uploaded to
|
||||
* @param {NodeJS.ReadableStream} openStream Stream of the file that will be uploaded
|
||||
* @param {number} start Starting byte index of file that the chunk belongs to
|
||||
* @param {number} end Ending byte index of file that the chunk belongs to
|
||||
* @param {number} uploadFileSize Total size of the file in bytes that is being uploaded
|
||||
* @param {boolean} isGzip Denotes if we are uploading a Gzip compressed stream
|
||||
* @param {number} totalFileSize Original total size of the file that is being uploaded
|
||||
* @returns if the chunk was successfully uploaded
|
||||
*/
|
||||
private uploadChunk;
|
||||
/**
|
||||
* Updates the size of the artifact from -1 which was initially set when the container was first created for the artifact.
|
||||
* Updating the size indicates that we are done uploading all the contents of the artifact
|
||||
*/
|
||||
patchArtifactSize(size: number, artifactName: string): Promise<void>;
|
||||
}
|
||||
+379
@@ -0,0 +1,379 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
||||
result["default"] = mod;
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const fs = __importStar(require("fs"));
|
||||
const core = __importStar(require("@actions/core"));
|
||||
const tmp = __importStar(require("tmp-promise"));
|
||||
const stream = __importStar(require("stream"));
|
||||
const utils_1 = require("./utils");
|
||||
const config_variables_1 = require("./config-variables");
|
||||
const util_1 = require("util");
|
||||
const url_1 = require("url");
|
||||
const perf_hooks_1 = require("perf_hooks");
|
||||
const status_reporter_1 = require("./status-reporter");
|
||||
const http_client_1 = require("@actions/http-client");
|
||||
const http_manager_1 = require("./http-manager");
|
||||
const upload_gzip_1 = require("./upload-gzip");
|
||||
const requestUtils_1 = require("./requestUtils");
|
||||
const stat = util_1.promisify(fs.stat);
|
||||
class UploadHttpClient {
|
||||
constructor() {
|
||||
this.uploadHttpManager = new http_manager_1.HttpManager(config_variables_1.getUploadFileConcurrency(), '@actions/artifact-upload');
|
||||
this.statusReporter = new status_reporter_1.StatusReporter(10000);
|
||||
}
|
||||
/**
|
||||
* Creates a file container for the new artifact in the remote blob storage/file service
|
||||
* @param {string} artifactName Name of the artifact being created
|
||||
* @returns The response from the Artifact Service if the file container was successfully created
|
||||
*/
|
||||
createArtifactInFileContainer(artifactName, options) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const parameters = {
|
||||
Type: 'actions_storage',
|
||||
Name: artifactName
|
||||
};
|
||||
// calculate retention period
|
||||
if (options && options.retentionDays) {
|
||||
const maxRetentionStr = config_variables_1.getRetentionDays();
|
||||
parameters.RetentionDays = utils_1.getProperRetention(options.retentionDays, maxRetentionStr);
|
||||
}
|
||||
const data = JSON.stringify(parameters, null, 2);
|
||||
const artifactUrl = utils_1.getArtifactUrl();
|
||||
// use the first client from the httpManager, `keep-alive` is not used so the connection will close immediately
|
||||
const client = this.uploadHttpManager.getClient(0);
|
||||
const headers = utils_1.getUploadHeaders('application/json', false);
|
||||
// Extra information to display when a particular HTTP code is returned
|
||||
// If a 403 is returned when trying to create a file container, the customer has exceeded
|
||||
// their storage quota so no new artifact containers can be created
|
||||
const customErrorMessages = new Map([
|
||||
[
|
||||
http_client_1.HttpCodes.Forbidden,
|
||||
'Artifact storage quota has been hit. Unable to upload any new artifacts'
|
||||
],
|
||||
[
|
||||
http_client_1.HttpCodes.BadRequest,
|
||||
`The artifact name ${artifactName} is not valid. Request URL ${artifactUrl}`
|
||||
]
|
||||
]);
|
||||
const response = yield requestUtils_1.retryHttpClientRequest('Create Artifact Container', () => __awaiter(this, void 0, void 0, function* () { return client.post(artifactUrl, data, headers); }), customErrorMessages);
|
||||
const body = yield response.readBody();
|
||||
return JSON.parse(body);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Concurrently upload all of the files in chunks
|
||||
* @param {string} uploadUrl Base Url for the artifact that was created
|
||||
* @param {SearchResult[]} filesToUpload A list of information about the files being uploaded
|
||||
* @returns The size of all the files uploaded in bytes
|
||||
*/
|
||||
uploadArtifactToFileContainer(uploadUrl, filesToUpload, options) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const FILE_CONCURRENCY = config_variables_1.getUploadFileConcurrency();
|
||||
const MAX_CHUNK_SIZE = config_variables_1.getUploadChunkSize();
|
||||
core.debug(`File Concurrency: ${FILE_CONCURRENCY}, and Chunk Size: ${MAX_CHUNK_SIZE}`);
|
||||
const parameters = [];
|
||||
// by default, file uploads will continue if there is an error unless specified differently in the options
|
||||
let continueOnError = true;
|
||||
if (options) {
|
||||
if (options.continueOnError === false) {
|
||||
continueOnError = false;
|
||||
}
|
||||
}
|
||||
// prepare the necessary parameters to upload all the files
|
||||
for (const file of filesToUpload) {
|
||||
const resourceUrl = new url_1.URL(uploadUrl);
|
||||
resourceUrl.searchParams.append('itemPath', file.uploadFilePath);
|
||||
parameters.push({
|
||||
file: file.absoluteFilePath,
|
||||
resourceUrl: resourceUrl.toString(),
|
||||
maxChunkSize: MAX_CHUNK_SIZE,
|
||||
continueOnError
|
||||
});
|
||||
}
|
||||
const parallelUploads = [...new Array(FILE_CONCURRENCY).keys()];
|
||||
const failedItemsToReport = [];
|
||||
let currentFile = 0;
|
||||
let completedFiles = 0;
|
||||
let uploadFileSize = 0;
|
||||
let totalFileSize = 0;
|
||||
let abortPendingFileUploads = false;
|
||||
this.statusReporter.setTotalNumberOfFilesToProcess(filesToUpload.length);
|
||||
this.statusReporter.start();
|
||||
// only allow a certain amount of files to be uploaded at once, this is done to reduce potential errors
|
||||
yield Promise.all(parallelUploads.map((index) => __awaiter(this, void 0, void 0, function* () {
|
||||
while (currentFile < filesToUpload.length) {
|
||||
const currentFileParameters = parameters[currentFile];
|
||||
currentFile += 1;
|
||||
if (abortPendingFileUploads) {
|
||||
failedItemsToReport.push(currentFileParameters.file);
|
||||
continue;
|
||||
}
|
||||
const startTime = perf_hooks_1.performance.now();
|
||||
const uploadFileResult = yield this.uploadFileAsync(index, currentFileParameters);
|
||||
if (core.isDebug()) {
|
||||
core.debug(`File: ${++completedFiles}/${filesToUpload.length}. ${currentFileParameters.file} took ${(perf_hooks_1.performance.now() - startTime).toFixed(3)} milliseconds to finish upload`);
|
||||
}
|
||||
uploadFileSize += uploadFileResult.successfulUploadSize;
|
||||
totalFileSize += uploadFileResult.totalSize;
|
||||
if (uploadFileResult.isSuccess === false) {
|
||||
failedItemsToReport.push(currentFileParameters.file);
|
||||
if (!continueOnError) {
|
||||
// fail fast
|
||||
core.error(`aborting artifact upload`);
|
||||
abortPendingFileUploads = true;
|
||||
}
|
||||
}
|
||||
this.statusReporter.incrementProcessedCount();
|
||||
}
|
||||
})));
|
||||
this.statusReporter.stop();
|
||||
// done uploading, safety dispose all connections
|
||||
this.uploadHttpManager.disposeAndReplaceAllClients();
|
||||
core.info(`Total size of all the files uploaded is ${uploadFileSize} bytes`);
|
||||
return {
|
||||
uploadSize: uploadFileSize,
|
||||
totalSize: totalFileSize,
|
||||
failedItems: failedItemsToReport
|
||||
};
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Asynchronously uploads a file. The file is compressed and uploaded using GZip if it is determined to save space.
|
||||
* If the upload file is bigger than the max chunk size it will be uploaded via multiple calls
|
||||
* @param {number} httpClientIndex The index of the httpClient that is being used to make all of the calls
|
||||
* @param {UploadFileParameters} parameters Information about the file that needs to be uploaded
|
||||
* @returns The size of the file that was uploaded in bytes along with any failed uploads
|
||||
*/
|
||||
uploadFileAsync(httpClientIndex, parameters) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const totalFileSize = (yield stat(parameters.file)).size;
|
||||
let offset = 0;
|
||||
let isUploadSuccessful = true;
|
||||
let failedChunkSizes = 0;
|
||||
let uploadFileSize = 0;
|
||||
let isGzip = true;
|
||||
// the file that is being uploaded is less than 64k in size, to increase throughput and to minimize disk I/O
|
||||
// for creating a new GZip file, an in-memory buffer is used for compression
|
||||
if (totalFileSize < 65536) {
|
||||
const buffer = yield upload_gzip_1.createGZipFileInBuffer(parameters.file);
|
||||
//An open stream is needed in the event of a failure and we need to retry. If a NodeJS.ReadableStream is directly passed in,
|
||||
// it will not properly get reset to the start of the stream if a chunk upload needs to be retried
|
||||
let openUploadStream;
|
||||
if (totalFileSize < buffer.byteLength) {
|
||||
// compression did not help with reducing the size, use a readable stream from the original file for upload
|
||||
openUploadStream = () => fs.createReadStream(parameters.file);
|
||||
isGzip = false;
|
||||
uploadFileSize = totalFileSize;
|
||||
}
|
||||
else {
|
||||
// create a readable stream using a PassThrough stream that is both readable and writable
|
||||
openUploadStream = () => {
|
||||
const passThrough = new stream.PassThrough();
|
||||
passThrough.end(buffer);
|
||||
return passThrough;
|
||||
};
|
||||
uploadFileSize = buffer.byteLength;
|
||||
}
|
||||
const result = yield this.uploadChunk(httpClientIndex, parameters.resourceUrl, openUploadStream, 0, uploadFileSize - 1, uploadFileSize, isGzip, totalFileSize);
|
||||
if (!result) {
|
||||
// chunk failed to upload
|
||||
isUploadSuccessful = false;
|
||||
failedChunkSizes += uploadFileSize;
|
||||
core.warning(`Aborting upload for ${parameters.file} due to failure`);
|
||||
}
|
||||
return {
|
||||
isSuccess: isUploadSuccessful,
|
||||
successfulUploadSize: uploadFileSize - failedChunkSizes,
|
||||
totalSize: totalFileSize
|
||||
};
|
||||
}
|
||||
else {
|
||||
// the file that is being uploaded is greater than 64k in size, a temporary file gets created on disk using the
|
||||
// npm tmp-promise package and this file gets used to create a GZipped file
|
||||
const tempFile = yield tmp.file();
|
||||
// create a GZip file of the original file being uploaded, the original file should not be modified in any way
|
||||
uploadFileSize = yield upload_gzip_1.createGZipFileOnDisk(parameters.file, tempFile.path);
|
||||
let uploadFilePath = tempFile.path;
|
||||
// compression did not help with size reduction, use the original file for upload and delete the temp GZip file
|
||||
if (totalFileSize < uploadFileSize) {
|
||||
uploadFileSize = totalFileSize;
|
||||
uploadFilePath = parameters.file;
|
||||
isGzip = false;
|
||||
}
|
||||
let abortFileUpload = false;
|
||||
// upload only a single chunk at a time
|
||||
while (offset < uploadFileSize) {
|
||||
const chunkSize = Math.min(uploadFileSize - offset, parameters.maxChunkSize);
|
||||
// if an individual file is greater than 100MB (1024*1024*100) in size, display extra information about the upload status
|
||||
if (uploadFileSize > 104857600) {
|
||||
this.statusReporter.updateLargeFileStatus(parameters.file, offset, uploadFileSize);
|
||||
}
|
||||
const start = offset;
|
||||
const end = offset + chunkSize - 1;
|
||||
offset += parameters.maxChunkSize;
|
||||
if (abortFileUpload) {
|
||||
// if we don't want to continue in the event of an error, any pending upload chunks will be marked as failed
|
||||
failedChunkSizes += chunkSize;
|
||||
continue;
|
||||
}
|
||||
const result = yield this.uploadChunk(httpClientIndex, parameters.resourceUrl, () => fs.createReadStream(uploadFilePath, {
|
||||
start,
|
||||
end,
|
||||
autoClose: false
|
||||
}), start, end, uploadFileSize, isGzip, totalFileSize);
|
||||
if (!result) {
|
||||
// Chunk failed to upload, report as failed and do not continue uploading any more chunks for the file. It is possible that part of a chunk was
|
||||
// successfully uploaded so the server may report a different size for what was uploaded
|
||||
isUploadSuccessful = false;
|
||||
failedChunkSizes += chunkSize;
|
||||
core.warning(`Aborting upload for ${parameters.file} due to failure`);
|
||||
abortFileUpload = true;
|
||||
}
|
||||
}
|
||||
// Delete the temporary file that was created as part of the upload. If the temp file does not get manually deleted by
|
||||
// calling cleanup, it gets removed when the node process exits. For more info see: https://www.npmjs.com/package/tmp-promise#about
|
||||
yield tempFile.cleanup();
|
||||
return {
|
||||
isSuccess: isUploadSuccessful,
|
||||
successfulUploadSize: uploadFileSize - failedChunkSizes,
|
||||
totalSize: totalFileSize
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Uploads a chunk of an individual file to the specified resourceUrl. If the upload fails and the status code
|
||||
* indicates a retryable status, we try to upload the chunk as well
|
||||
* @param {number} httpClientIndex The index of the httpClient being used to make all the necessary calls
|
||||
* @param {string} resourceUrl Url of the resource that the chunk will be uploaded to
|
||||
* @param {NodeJS.ReadableStream} openStream Stream of the file that will be uploaded
|
||||
* @param {number} start Starting byte index of file that the chunk belongs to
|
||||
* @param {number} end Ending byte index of file that the chunk belongs to
|
||||
* @param {number} uploadFileSize Total size of the file in bytes that is being uploaded
|
||||
* @param {boolean} isGzip Denotes if we are uploading a Gzip compressed stream
|
||||
* @param {number} totalFileSize Original total size of the file that is being uploaded
|
||||
* @returns if the chunk was successfully uploaded
|
||||
*/
|
||||
uploadChunk(httpClientIndex, resourceUrl, openStream, start, end, uploadFileSize, isGzip, totalFileSize) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
// prepare all the necessary headers before making any http call
|
||||
const headers = utils_1.getUploadHeaders('application/octet-stream', true, isGzip, totalFileSize, end - start + 1, utils_1.getContentRange(start, end, uploadFileSize));
|
||||
const uploadChunkRequest = () => __awaiter(this, void 0, void 0, function* () {
|
||||
const client = this.uploadHttpManager.getClient(httpClientIndex);
|
||||
return yield client.sendStream('PUT', resourceUrl, openStream(), headers);
|
||||
});
|
||||
let retryCount = 0;
|
||||
const retryLimit = config_variables_1.getRetryLimit();
|
||||
// Increments the current retry count and then checks if the retry limit has been reached
|
||||
// If there have been too many retries, fail so the download stops
|
||||
const incrementAndCheckRetryLimit = (response) => {
|
||||
retryCount++;
|
||||
if (retryCount > retryLimit) {
|
||||
if (response) {
|
||||
utils_1.displayHttpDiagnostics(response);
|
||||
}
|
||||
core.info(`Retry limit has been reached for chunk at offset ${start} to ${resourceUrl}`);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const backOff = (retryAfterValue) => __awaiter(this, void 0, void 0, function* () {
|
||||
this.uploadHttpManager.disposeAndReplaceClient(httpClientIndex);
|
||||
if (retryAfterValue) {
|
||||
core.info(`Backoff due to too many requests, retry #${retryCount}. Waiting for ${retryAfterValue} milliseconds before continuing the upload`);
|
||||
yield utils_1.sleep(retryAfterValue);
|
||||
}
|
||||
else {
|
||||
const backoffTime = utils_1.getExponentialRetryTimeInMilliseconds(retryCount);
|
||||
core.info(`Exponential backoff for retry #${retryCount}. Waiting for ${backoffTime} milliseconds before continuing the upload at offset ${start}`);
|
||||
yield utils_1.sleep(backoffTime);
|
||||
}
|
||||
core.info(`Finished backoff for retry #${retryCount}, continuing with upload`);
|
||||
return;
|
||||
});
|
||||
// allow for failed chunks to be retried multiple times
|
||||
while (retryCount <= retryLimit) {
|
||||
let response;
|
||||
try {
|
||||
response = yield uploadChunkRequest();
|
||||
}
|
||||
catch (error) {
|
||||
// if an error is caught, it is usually indicative of a timeout so retry the upload
|
||||
core.info(`An error has been caught http-client index ${httpClientIndex}, retrying the upload`);
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
if (incrementAndCheckRetryLimit()) {
|
||||
return false;
|
||||
}
|
||||
yield backOff();
|
||||
continue;
|
||||
}
|
||||
// Always read the body of the response. There is potential for a resource leak if the body is not read which will
|
||||
// result in the connection remaining open along with unintended consequences when trying to dispose of the client
|
||||
yield response.readBody();
|
||||
if (utils_1.isSuccessStatusCode(response.message.statusCode)) {
|
||||
return true;
|
||||
}
|
||||
else if (utils_1.isRetryableStatusCode(response.message.statusCode)) {
|
||||
core.info(`A ${response.message.statusCode} status code has been received, will attempt to retry the upload`);
|
||||
if (incrementAndCheckRetryLimit(response)) {
|
||||
return false;
|
||||
}
|
||||
utils_1.isThrottledStatusCode(response.message.statusCode)
|
||||
? yield backOff(utils_1.tryGetRetryAfterValueTimeInMilliseconds(response.message.headers))
|
||||
: yield backOff();
|
||||
}
|
||||
else {
|
||||
core.error(`Unexpected response. Unable to upload chunk to ${resourceUrl}`);
|
||||
utils_1.displayHttpDiagnostics(response);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Updates the size of the artifact from -1 which was initially set when the container was first created for the artifact.
|
||||
* Updating the size indicates that we are done uploading all the contents of the artifact
|
||||
*/
|
||||
patchArtifactSize(size, artifactName) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const resourceUrl = new url_1.URL(utils_1.getArtifactUrl());
|
||||
resourceUrl.searchParams.append('artifactName', artifactName);
|
||||
const parameters = { Size: size };
|
||||
const data = JSON.stringify(parameters, null, 2);
|
||||
core.debug(`URL is ${resourceUrl.toString()}`);
|
||||
// use the first client from the httpManager, `keep-alive` is not used so the connection will close immediately
|
||||
const client = this.uploadHttpManager.getClient(0);
|
||||
const headers = utils_1.getUploadHeaders('application/json', false);
|
||||
// Extra information to display when a particular HTTP code is returned
|
||||
const customErrorMessages = new Map([
|
||||
[
|
||||
http_client_1.HttpCodes.NotFound,
|
||||
`An Artifact with the name ${artifactName} was not found`
|
||||
]
|
||||
]);
|
||||
// TODO retry for all possible response codes, the artifact upload is pretty much complete so it at all costs we should try to finish this
|
||||
const response = yield requestUtils_1.retryHttpClientRequest('Finalize artifact upload', () => __awaiter(this, void 0, void 0, function* () { return client.patch(resourceUrl.toString(), data, headers); }), customErrorMessages);
|
||||
yield response.readBody();
|
||||
core.debug(`Artifact ${artifactName} has been successfully uploaded, total size in bytes: ${size}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.UploadHttpClient = UploadHttpClient;
|
||||
//# sourceMappingURL=upload-http-client.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
+34
@@ -0,0 +1,34 @@
|
||||
export interface UploadOptions {
|
||||
/**
|
||||
* Indicates if the artifact upload should continue if file or chunk fails to upload from any error.
|
||||
* If there is a error during upload, a partial artifact will always be associated and available for
|
||||
* download at the end. The size reported will be the amount of storage that the user or org will be
|
||||
* charged for the partial artifact. Defaults to true if not specified
|
||||
*
|
||||
* If set to false, and an error is encountered, all other uploads will stop and any files or chunks
|
||||
* that were queued will not be attempted to be uploaded. The partial artifact available will only
|
||||
* include files and chunks up until the failure
|
||||
*
|
||||
* If set to true and an error is encountered, the failed file will be skipped and ignored and all
|
||||
* other queued files will be attempted to be uploaded. The partial artifact at the end will have all
|
||||
* files with the exception of the problematic files(s)/chunks(s) that failed to upload
|
||||
*
|
||||
*/
|
||||
continueOnError?: boolean;
|
||||
/**
|
||||
* Duration after which artifact will expire in days.
|
||||
*
|
||||
* By default artifact expires after 90 days:
|
||||
* https://docs.github.com/en/actions/configuring-and-managing-workflows/persisting-workflow-data-using-artifacts#downloading-and-deleting-artifacts-after-a-workflow-run-is-complete
|
||||
*
|
||||
* Use this option to override the default expiry.
|
||||
*
|
||||
* Min value: 1
|
||||
* Max value: 90 unless changed by repository setting
|
||||
*
|
||||
* If this is set to a greater value than the retention settings allowed, the retention on artifacts
|
||||
* will be reduced to match the max value allowed on server, and the upload process will continue. An
|
||||
* input of 0 assumes default retention setting.
|
||||
*/
|
||||
retentionDays?: number;
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
//# sourceMappingURL=upload-options.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"upload-options.js","sourceRoot":"","sources":["../../src/internal/upload-options.ts"],"names":[],"mappings":""}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
export interface UploadResponse {
|
||||
/**
|
||||
* The name of the artifact that was uploaded
|
||||
*/
|
||||
artifactName: string;
|
||||
/**
|
||||
* A list of all items that are meant to be uploaded as part of the artifact
|
||||
*/
|
||||
artifactItems: string[];
|
||||
/**
|
||||
* Total size of the artifact in bytes that was uploaded
|
||||
*/
|
||||
size: number;
|
||||
/**
|
||||
* A list of items that were not uploaded as part of the artifact (includes queued items that were not uploaded if
|
||||
* continueOnError is set to false). This is a subset of artifactItems.
|
||||
*/
|
||||
failedItems: string[];
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
//# sourceMappingURL=upload-response.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"upload-response.js","sourceRoot":"","sources":["../../src/internal/upload-response.ts"],"names":[],"mappings":""}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
export interface UploadSpecification {
|
||||
absoluteFilePath: string;
|
||||
uploadFilePath: string;
|
||||
}
|
||||
/**
|
||||
* Creates a specification that describes how each file that is part of the artifact will be uploaded
|
||||
* @param artifactName the name of the artifact being uploaded. Used during upload to denote where the artifact is stored on the server
|
||||
* @param rootDirectory an absolute file path that denotes the path that should be removed from the beginning of each artifact file
|
||||
* @param artifactFiles a list of absolute file paths that denote what should be uploaded as part of the artifact
|
||||
*/
|
||||
export declare function getUploadSpecification(artifactName: string, rootDirectory: string, artifactFiles: string[]): UploadSpecification[];
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
"use strict";
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
||||
result["default"] = mod;
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const fs = __importStar(require("fs"));
|
||||
const core_1 = require("@actions/core");
|
||||
const path_1 = require("path");
|
||||
const utils_1 = require("./utils");
|
||||
/**
|
||||
* Creates a specification that describes how each file that is part of the artifact will be uploaded
|
||||
* @param artifactName the name of the artifact being uploaded. Used during upload to denote where the artifact is stored on the server
|
||||
* @param rootDirectory an absolute file path that denotes the path that should be removed from the beginning of each artifact file
|
||||
* @param artifactFiles a list of absolute file paths that denote what should be uploaded as part of the artifact
|
||||
*/
|
||||
function getUploadSpecification(artifactName, rootDirectory, artifactFiles) {
|
||||
utils_1.checkArtifactName(artifactName);
|
||||
const specifications = [];
|
||||
if (!fs.existsSync(rootDirectory)) {
|
||||
throw new Error(`Provided rootDirectory ${rootDirectory} does not exist`);
|
||||
}
|
||||
if (!fs.lstatSync(rootDirectory).isDirectory()) {
|
||||
throw new Error(`Provided rootDirectory ${rootDirectory} is not a valid directory`);
|
||||
}
|
||||
// Normalize and resolve, this allows for either absolute or relative paths to be used
|
||||
rootDirectory = path_1.normalize(rootDirectory);
|
||||
rootDirectory = path_1.resolve(rootDirectory);
|
||||
/*
|
||||
Example to demonstrate behavior
|
||||
|
||||
Input:
|
||||
artifactName: my-artifact
|
||||
rootDirectory: '/home/user/files/plz-upload'
|
||||
artifactFiles: [
|
||||
'/home/user/files/plz-upload/file1.txt',
|
||||
'/home/user/files/plz-upload/file2.txt',
|
||||
'/home/user/files/plz-upload/dir/file3.txt'
|
||||
]
|
||||
|
||||
Output:
|
||||
specifications: [
|
||||
['/home/user/files/plz-upload/file1.txt', 'my-artifact/file1.txt'],
|
||||
['/home/user/files/plz-upload/file1.txt', 'my-artifact/file2.txt'],
|
||||
['/home/user/files/plz-upload/file1.txt', 'my-artifact/dir/file3.txt']
|
||||
]
|
||||
*/
|
||||
for (let file of artifactFiles) {
|
||||
if (!fs.existsSync(file)) {
|
||||
throw new Error(`File ${file} does not exist`);
|
||||
}
|
||||
if (!fs.lstatSync(file).isDirectory()) {
|
||||
// Normalize and resolve, this allows for either absolute or relative paths to be used
|
||||
file = path_1.normalize(file);
|
||||
file = path_1.resolve(file);
|
||||
if (!file.startsWith(rootDirectory)) {
|
||||
throw new Error(`The rootDirectory: ${rootDirectory} is not a parent directory of the file: ${file}`);
|
||||
}
|
||||
// Check for forbidden characters in file paths that will be rejected during upload
|
||||
const uploadPath = file.replace(rootDirectory, '');
|
||||
utils_1.checkArtifactFilePath(uploadPath);
|
||||
/*
|
||||
uploadFilePath denotes where the file will be uploaded in the file container on the server. During a run, if multiple artifacts are uploaded, they will all
|
||||
be saved in the same container. The artifact name is used as the root directory in the container to separate and distinguish uploaded artifacts
|
||||
|
||||
path.join handles all the following cases and would return 'artifact-name/file-to-upload.txt
|
||||
join('artifact-name/', 'file-to-upload.txt')
|
||||
join('artifact-name/', '/file-to-upload.txt')
|
||||
join('artifact-name', 'file-to-upload.txt')
|
||||
join('artifact-name', '/file-to-upload.txt')
|
||||
*/
|
||||
specifications.push({
|
||||
absoluteFilePath: file,
|
||||
uploadFilePath: path_1.join(artifactName, uploadPath)
|
||||
});
|
||||
}
|
||||
else {
|
||||
// Directories are rejected by the server during upload
|
||||
core_1.debug(`Removing ${file} from rawSearchResults because it is a directory`);
|
||||
}
|
||||
}
|
||||
return specifications;
|
||||
}
|
||||
exports.getUploadSpecification = getUploadSpecification;
|
||||
//# sourceMappingURL=upload-specification.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"upload-specification.js","sourceRoot":"","sources":["../../src/internal/upload-specification.ts"],"names":[],"mappings":";;;;;;;;;AAAA,uCAAwB;AACxB,wCAAmC;AACnC,+BAA6C;AAC7C,mCAAgE;AAOhE;;;;;GAKG;AACH,SAAgB,sBAAsB,CACpC,YAAoB,EACpB,aAAqB,EACrB,aAAuB;IAEvB,yBAAiB,CAAC,YAAY,CAAC,CAAA;IAE/B,MAAM,cAAc,GAA0B,EAAE,CAAA;IAEhD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE;QACjC,MAAM,IAAI,KAAK,CAAC,0BAA0B,aAAa,iBAAiB,CAAC,CAAA;KAC1E;IACD,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC,WAAW,EAAE,EAAE;QAC9C,MAAM,IAAI,KAAK,CACb,0BAA0B,aAAa,2BAA2B,CACnE,CAAA;KACF;IACD,sFAAsF;IACtF,aAAa,GAAG,gBAAS,CAAC,aAAa,CAAC,CAAA;IACxC,aAAa,GAAG,cAAO,CAAC,aAAa,CAAC,CAAA;IAEtC;;;;;;;;;;;;;;;;;;MAkBE;IACF,KAAK,IAAI,IAAI,IAAI,aAAa,EAAE;QAC9B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE;YACxB,MAAM,IAAI,KAAK,CAAC,QAAQ,IAAI,iBAAiB,CAAC,CAAA;SAC/C;QACD,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,EAAE;YACrC,sFAAsF;YACtF,IAAI,GAAG,gBAAS,CAAC,IAAI,CAAC,CAAA;YACtB,IAAI,GAAG,cAAO,CAAC,IAAI,CAAC,CAAA;YACpB,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE;gBACnC,MAAM,IAAI,KAAK,CACb,sBAAsB,aAAa,2CAA2C,IAAI,EAAE,CACrF,CAAA;aACF;YAED,mFAAmF;YACnF,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAA;YAClD,6BAAqB,CAAC,UAAU,CAAC,CAAA;YAEjC;;;;;;;;;cASE;YACF,cAAc,CAAC,IAAI,CAAC;gBAClB,gBAAgB,EAAE,IAAI;gBACtB,cAAc,EAAE,WAAI,CAAC,YAAY,EAAE,UAAU,CAAC;aAC/C,CAAC,CAAA;SACH;aAAM;YACL,uDAAuD;YACvD,YAAK,CAAC,YAAY,IAAI,kDAAkD,CAAC,CAAA;SAC1E;KACF;IACD,OAAO,cAAc,CAAA;AACvB,CAAC;AA9ED,wDA8EC"}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
/// <reference types="node" />
|
||||
import { HttpClient } from '@actions/http-client';
|
||||
import { IHeaders, IHttpClientResponse } from '@actions/http-client/interfaces';
|
||||
import { IncomingHttpHeaders } from 'http';
|
||||
/**
|
||||
* Returns a retry time in milliseconds that exponentially gets larger
|
||||
* depending on the amount of retries that have been attempted
|
||||
*/
|
||||
export declare function getExponentialRetryTimeInMilliseconds(retryCount: number): number;
|
||||
/**
|
||||
* Parses a env variable that is a number
|
||||
*/
|
||||
export declare function parseEnvNumber(key: string): number | undefined;
|
||||
/**
|
||||
* Various utility functions to help with the necessary API calls
|
||||
*/
|
||||
export declare function getApiVersion(): string;
|
||||
export declare function isSuccessStatusCode(statusCode?: number): boolean;
|
||||
export declare function isForbiddenStatusCode(statusCode?: number): boolean;
|
||||
export declare function isRetryableStatusCode(statusCode: number | undefined): boolean;
|
||||
export declare function isThrottledStatusCode(statusCode?: number): boolean;
|
||||
/**
|
||||
* Attempts to get the retry-after value from a set of http headers. The retry time
|
||||
* is originally denoted in seconds, so if present, it is converted to milliseconds
|
||||
* @param headers all the headers received when making an http call
|
||||
*/
|
||||
export declare function tryGetRetryAfterValueTimeInMilliseconds(headers: IncomingHttpHeaders): number | undefined;
|
||||
export declare function getContentRange(start: number, end: number, total: number): string;
|
||||
/**
|
||||
* Sets all the necessary headers when downloading an artifact
|
||||
* @param {string} contentType the type of content being uploaded
|
||||
* @param {boolean} isKeepAlive is the same connection being used to make multiple calls
|
||||
* @param {boolean} acceptGzip can we accept a gzip encoded response
|
||||
* @param {string} acceptType the type of content that we can accept
|
||||
* @returns appropriate headers to make a specific http call during artifact download
|
||||
*/
|
||||
export declare function getDownloadHeaders(contentType: string, isKeepAlive?: boolean, acceptGzip?: boolean): IHeaders;
|
||||
/**
|
||||
* Sets all the necessary headers when uploading an artifact
|
||||
* @param {string} contentType the type of content being uploaded
|
||||
* @param {boolean} isKeepAlive is the same connection being used to make multiple calls
|
||||
* @param {boolean} isGzip is the connection being used to upload GZip compressed content
|
||||
* @param {number} uncompressedLength the original size of the content if something is being uploaded that has been compressed
|
||||
* @param {number} contentLength the length of the content that is being uploaded
|
||||
* @param {string} contentRange the range of the content that is being uploaded
|
||||
* @returns appropriate headers to make a specific http call during artifact upload
|
||||
*/
|
||||
export declare function getUploadHeaders(contentType: string, isKeepAlive?: boolean, isGzip?: boolean, uncompressedLength?: number, contentLength?: number, contentRange?: string): IHeaders;
|
||||
export declare function createHttpClient(userAgent: string): HttpClient;
|
||||
export declare function getArtifactUrl(): string;
|
||||
/**
|
||||
* Uh oh! Something might have gone wrong during either upload or download. The IHtttpClientResponse object contains information
|
||||
* about the http call that was made by the actions http client. This information might be useful to display for diagnostic purposes, but
|
||||
* this entire object is really big and most of the information is not really useful. This function takes the response object and displays only
|
||||
* the information that we want.
|
||||
*
|
||||
* Certain information such as the TLSSocket and the Readable state are not really useful for diagnostic purposes so they can be avoided.
|
||||
* Other information such as the headers, the response code and message might be useful, so this is displayed.
|
||||
*/
|
||||
export declare function displayHttpDiagnostics(response: IHttpClientResponse): void;
|
||||
/**
|
||||
* Scans the name of the artifact to make sure there are no illegal characters
|
||||
*/
|
||||
export declare function checkArtifactName(name: string): void;
|
||||
/**
|
||||
* Scans the name of the filePath used to make sure there are no illegal characters
|
||||
*/
|
||||
export declare function checkArtifactFilePath(path: string): void;
|
||||
export declare function createDirectoriesForArtifact(directories: string[]): Promise<void>;
|
||||
export declare function createEmptyFilesForArtifact(emptyFilesToCreate: string[]): Promise<void>;
|
||||
export declare function getFileSize(filePath: string): Promise<number>;
|
||||
export declare function rmFile(filePath: string): Promise<void>;
|
||||
export declare function getProperRetention(retentionInput: number, retentionSetting: string | undefined): number;
|
||||
export declare function sleep(milliseconds: number): Promise<void>;
|
||||
+304
@@ -0,0 +1,304 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const core_1 = require("@actions/core");
|
||||
const fs_1 = require("fs");
|
||||
const http_client_1 = require("@actions/http-client");
|
||||
const auth_1 = require("@actions/http-client/auth");
|
||||
const config_variables_1 = require("./config-variables");
|
||||
/**
|
||||
* Returns a retry time in milliseconds that exponentially gets larger
|
||||
* depending on the amount of retries that have been attempted
|
||||
*/
|
||||
function getExponentialRetryTimeInMilliseconds(retryCount) {
|
||||
if (retryCount < 0) {
|
||||
throw new Error('RetryCount should not be negative');
|
||||
}
|
||||
else if (retryCount === 0) {
|
||||
return config_variables_1.getInitialRetryIntervalInMilliseconds();
|
||||
}
|
||||
const minTime = config_variables_1.getInitialRetryIntervalInMilliseconds() * config_variables_1.getRetryMultiplier() * retryCount;
|
||||
const maxTime = minTime * config_variables_1.getRetryMultiplier();
|
||||
// returns a random number between the minTime (inclusive) and the maxTime (exclusive)
|
||||
return Math.random() * (maxTime - minTime) + minTime;
|
||||
}
|
||||
exports.getExponentialRetryTimeInMilliseconds = getExponentialRetryTimeInMilliseconds;
|
||||
/**
|
||||
* Parses a env variable that is a number
|
||||
*/
|
||||
function parseEnvNumber(key) {
|
||||
const value = Number(process.env[key]);
|
||||
if (Number.isNaN(value) || value < 0) {
|
||||
return undefined;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
exports.parseEnvNumber = parseEnvNumber;
|
||||
/**
|
||||
* Various utility functions to help with the necessary API calls
|
||||
*/
|
||||
function getApiVersion() {
|
||||
return '6.0-preview';
|
||||
}
|
||||
exports.getApiVersion = getApiVersion;
|
||||
function isSuccessStatusCode(statusCode) {
|
||||
if (!statusCode) {
|
||||
return false;
|
||||
}
|
||||
return statusCode >= 200 && statusCode < 300;
|
||||
}
|
||||
exports.isSuccessStatusCode = isSuccessStatusCode;
|
||||
function isForbiddenStatusCode(statusCode) {
|
||||
if (!statusCode) {
|
||||
return false;
|
||||
}
|
||||
return statusCode === http_client_1.HttpCodes.Forbidden;
|
||||
}
|
||||
exports.isForbiddenStatusCode = isForbiddenStatusCode;
|
||||
function isRetryableStatusCode(statusCode) {
|
||||
if (!statusCode) {
|
||||
return false;
|
||||
}
|
||||
const retryableStatusCodes = [
|
||||
http_client_1.HttpCodes.BadGateway,
|
||||
http_client_1.HttpCodes.ServiceUnavailable,
|
||||
http_client_1.HttpCodes.GatewayTimeout,
|
||||
http_client_1.HttpCodes.TooManyRequests,
|
||||
413 // Payload Too Large
|
||||
];
|
||||
return retryableStatusCodes.includes(statusCode);
|
||||
}
|
||||
exports.isRetryableStatusCode = isRetryableStatusCode;
|
||||
function isThrottledStatusCode(statusCode) {
|
||||
if (!statusCode) {
|
||||
return false;
|
||||
}
|
||||
return statusCode === http_client_1.HttpCodes.TooManyRequests;
|
||||
}
|
||||
exports.isThrottledStatusCode = isThrottledStatusCode;
|
||||
/**
|
||||
* Attempts to get the retry-after value from a set of http headers. The retry time
|
||||
* is originally denoted in seconds, so if present, it is converted to milliseconds
|
||||
* @param headers all the headers received when making an http call
|
||||
*/
|
||||
function tryGetRetryAfterValueTimeInMilliseconds(headers) {
|
||||
if (headers['retry-after']) {
|
||||
const retryTime = Number(headers['retry-after']);
|
||||
if (!isNaN(retryTime)) {
|
||||
core_1.info(`Retry-After header is present with a value of ${retryTime}`);
|
||||
return retryTime * 1000;
|
||||
}
|
||||
core_1.info(`Returned retry-after header value: ${retryTime} is non-numeric and cannot be used`);
|
||||
return undefined;
|
||||
}
|
||||
core_1.info(`No retry-after header was found. Dumping all headers for diagnostic purposes`);
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(headers);
|
||||
return undefined;
|
||||
}
|
||||
exports.tryGetRetryAfterValueTimeInMilliseconds = tryGetRetryAfterValueTimeInMilliseconds;
|
||||
function getContentRange(start, end, total) {
|
||||
// Format: `bytes start-end/fileSize
|
||||
// start and end are inclusive
|
||||
// For a 200 byte chunk starting at byte 0:
|
||||
// Content-Range: bytes 0-199/200
|
||||
return `bytes ${start}-${end}/${total}`;
|
||||
}
|
||||
exports.getContentRange = getContentRange;
|
||||
/**
|
||||
* Sets all the necessary headers when downloading an artifact
|
||||
* @param {string} contentType the type of content being uploaded
|
||||
* @param {boolean} isKeepAlive is the same connection being used to make multiple calls
|
||||
* @param {boolean} acceptGzip can we accept a gzip encoded response
|
||||
* @param {string} acceptType the type of content that we can accept
|
||||
* @returns appropriate headers to make a specific http call during artifact download
|
||||
*/
|
||||
function getDownloadHeaders(contentType, isKeepAlive, acceptGzip) {
|
||||
const requestOptions = {};
|
||||
if (contentType) {
|
||||
requestOptions['Content-Type'] = contentType;
|
||||
}
|
||||
if (isKeepAlive) {
|
||||
requestOptions['Connection'] = 'Keep-Alive';
|
||||
// keep alive for at least 10 seconds before closing the connection
|
||||
requestOptions['Keep-Alive'] = '10';
|
||||
}
|
||||
if (acceptGzip) {
|
||||
// if we are expecting a response with gzip encoding, it should be using an octet-stream in the accept header
|
||||
requestOptions['Accept-Encoding'] = 'gzip';
|
||||
requestOptions['Accept'] = `application/octet-stream;api-version=${getApiVersion()}`;
|
||||
}
|
||||
else {
|
||||
// default to application/json if we are not working with gzip content
|
||||
requestOptions['Accept'] = `application/json;api-version=${getApiVersion()}`;
|
||||
}
|
||||
return requestOptions;
|
||||
}
|
||||
exports.getDownloadHeaders = getDownloadHeaders;
|
||||
/**
|
||||
* Sets all the necessary headers when uploading an artifact
|
||||
* @param {string} contentType the type of content being uploaded
|
||||
* @param {boolean} isKeepAlive is the same connection being used to make multiple calls
|
||||
* @param {boolean} isGzip is the connection being used to upload GZip compressed content
|
||||
* @param {number} uncompressedLength the original size of the content if something is being uploaded that has been compressed
|
||||
* @param {number} contentLength the length of the content that is being uploaded
|
||||
* @param {string} contentRange the range of the content that is being uploaded
|
||||
* @returns appropriate headers to make a specific http call during artifact upload
|
||||
*/
|
||||
function getUploadHeaders(contentType, isKeepAlive, isGzip, uncompressedLength, contentLength, contentRange) {
|
||||
const requestOptions = {};
|
||||
requestOptions['Accept'] = `application/json;api-version=${getApiVersion()}`;
|
||||
if (contentType) {
|
||||
requestOptions['Content-Type'] = contentType;
|
||||
}
|
||||
if (isKeepAlive) {
|
||||
requestOptions['Connection'] = 'Keep-Alive';
|
||||
// keep alive for at least 10 seconds before closing the connection
|
||||
requestOptions['Keep-Alive'] = '10';
|
||||
}
|
||||
if (isGzip) {
|
||||
requestOptions['Content-Encoding'] = 'gzip';
|
||||
requestOptions['x-tfs-filelength'] = uncompressedLength;
|
||||
}
|
||||
if (contentLength) {
|
||||
requestOptions['Content-Length'] = contentLength;
|
||||
}
|
||||
if (contentRange) {
|
||||
requestOptions['Content-Range'] = contentRange;
|
||||
}
|
||||
return requestOptions;
|
||||
}
|
||||
exports.getUploadHeaders = getUploadHeaders;
|
||||
function createHttpClient(userAgent) {
|
||||
return new http_client_1.HttpClient(userAgent, [
|
||||
new auth_1.BearerCredentialHandler(config_variables_1.getRuntimeToken())
|
||||
]);
|
||||
}
|
||||
exports.createHttpClient = createHttpClient;
|
||||
function getArtifactUrl() {
|
||||
const artifactUrl = `${config_variables_1.getRuntimeUrl()}_apis/pipelines/workflows/${config_variables_1.getWorkFlowRunId()}/artifacts?api-version=${getApiVersion()}`;
|
||||
core_1.debug(`Artifact Url: ${artifactUrl}`);
|
||||
return artifactUrl;
|
||||
}
|
||||
exports.getArtifactUrl = getArtifactUrl;
|
||||
/**
|
||||
* Uh oh! Something might have gone wrong during either upload or download. The IHtttpClientResponse object contains information
|
||||
* about the http call that was made by the actions http client. This information might be useful to display for diagnostic purposes, but
|
||||
* this entire object is really big and most of the information is not really useful. This function takes the response object and displays only
|
||||
* the information that we want.
|
||||
*
|
||||
* Certain information such as the TLSSocket and the Readable state are not really useful for diagnostic purposes so they can be avoided.
|
||||
* Other information such as the headers, the response code and message might be useful, so this is displayed.
|
||||
*/
|
||||
function displayHttpDiagnostics(response) {
|
||||
core_1.info(`##### Begin Diagnostic HTTP information #####
|
||||
Status Code: ${response.message.statusCode}
|
||||
Status Message: ${response.message.statusMessage}
|
||||
Header Information: ${JSON.stringify(response.message.headers, undefined, 2)}
|
||||
###### End Diagnostic HTTP information ######`);
|
||||
}
|
||||
exports.displayHttpDiagnostics = displayHttpDiagnostics;
|
||||
/**
|
||||
* Invalid characters that cannot be in the artifact name or an uploaded file. Will be rejected
|
||||
* from the server if attempted to be sent over. These characters are not allowed due to limitations with certain
|
||||
* file systems such as NTFS. To maintain platform-agnostic behavior, all characters that are not supported by an
|
||||
* individual filesystem/platform will not be supported on all fileSystems/platforms
|
||||
*
|
||||
* FilePaths can include characters such as \ and / which are not permitted in the artifact name alone
|
||||
*/
|
||||
const invalidArtifactFilePathCharacters = ['"', ':', '<', '>', '|', '*', '?'];
|
||||
const invalidArtifactNameCharacters = [
|
||||
...invalidArtifactFilePathCharacters,
|
||||
'\\',
|
||||
'/'
|
||||
];
|
||||
/**
|
||||
* Scans the name of the artifact to make sure there are no illegal characters
|
||||
*/
|
||||
function checkArtifactName(name) {
|
||||
if (!name) {
|
||||
throw new Error(`Artifact name: ${name}, is incorrectly provided`);
|
||||
}
|
||||
for (const invalidChar of invalidArtifactNameCharacters) {
|
||||
if (name.includes(invalidChar)) {
|
||||
throw new Error(`Artifact name is not valid: ${name}. Contains character: "${invalidChar}". Invalid artifact name characters include: ${invalidArtifactNameCharacters.toString()}.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.checkArtifactName = checkArtifactName;
|
||||
/**
|
||||
* Scans the name of the filePath used to make sure there are no illegal characters
|
||||
*/
|
||||
function checkArtifactFilePath(path) {
|
||||
if (!path) {
|
||||
throw new Error(`Artifact path: ${path}, is incorrectly provided`);
|
||||
}
|
||||
for (const invalidChar of invalidArtifactFilePathCharacters) {
|
||||
if (path.includes(invalidChar)) {
|
||||
throw new Error(`Artifact path is not valid: ${path}. Contains character: "${invalidChar}". Invalid characters include: ${invalidArtifactFilePathCharacters.toString()}.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.checkArtifactFilePath = checkArtifactFilePath;
|
||||
function createDirectoriesForArtifact(directories) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
for (const directory of directories) {
|
||||
yield fs_1.promises.mkdir(directory, {
|
||||
recursive: true
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
exports.createDirectoriesForArtifact = createDirectoriesForArtifact;
|
||||
function createEmptyFilesForArtifact(emptyFilesToCreate) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
for (const filePath of emptyFilesToCreate) {
|
||||
yield (yield fs_1.promises.open(filePath, 'w')).close();
|
||||
}
|
||||
});
|
||||
}
|
||||
exports.createEmptyFilesForArtifact = createEmptyFilesForArtifact;
|
||||
function getFileSize(filePath) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const stats = yield fs_1.promises.stat(filePath);
|
||||
core_1.debug(`${filePath} size:(${stats.size}) blksize:(${stats.blksize}) blocks:(${stats.blocks})`);
|
||||
return stats.size;
|
||||
});
|
||||
}
|
||||
exports.getFileSize = getFileSize;
|
||||
function rmFile(filePath) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
yield fs_1.promises.unlink(filePath);
|
||||
});
|
||||
}
|
||||
exports.rmFile = rmFile;
|
||||
function getProperRetention(retentionInput, retentionSetting) {
|
||||
if (retentionInput < 0) {
|
||||
throw new Error('Invalid retention, minimum value is 1.');
|
||||
}
|
||||
let retention = retentionInput;
|
||||
if (retentionSetting) {
|
||||
const maxRetention = parseInt(retentionSetting);
|
||||
if (!isNaN(maxRetention) && maxRetention < retention) {
|
||||
core_1.warning(`Retention days is greater than the max value allowed by the repository setting, reduce retention to ${maxRetention} days`);
|
||||
retention = maxRetention;
|
||||
}
|
||||
}
|
||||
return retention;
|
||||
}
|
||||
exports.getProperRetention = getProperRetention;
|
||||
function sleep(milliseconds) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
return new Promise(resolve => setTimeout(resolve, milliseconds));
|
||||
});
|
||||
}
|
||||
exports.sleep = sleep;
|
||||
//# sourceMappingURL=utils.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
+21
@@ -0,0 +1,21 @@
|
||||
Actions Http Client for Node.js
|
||||
|
||||
Copyright (c) GitHub, Inc.
|
||||
|
||||
All rights reserved.
|
||||
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||
associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
||||
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
|
||||
<p align="center">
|
||||
<img src="actions.png">
|
||||
</p>
|
||||
|
||||
# Actions Http-Client
|
||||
|
||||
[](https://github.com/actions/http-client/actions)
|
||||
|
||||
A lightweight HTTP client optimized for use with actions, TypeScript with generics and async await.
|
||||
|
||||
## Features
|
||||
|
||||
- HTTP client with TypeScript generics and async/await/Promises
|
||||
- Typings included so no need to acquire separately (great for intellisense and no versioning drift)
|
||||
- [Proxy support](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/about-self-hosted-runners#using-a-proxy-server-with-self-hosted-runners) just works with actions and the runner
|
||||
- Targets ES2019 (runner runs actions with node 12+). Only supported on node 12+.
|
||||
- Basic, Bearer and PAT Support out of the box. Extensible handlers for others.
|
||||
- Redirects supported
|
||||
|
||||
Features and releases [here](./RELEASES.md)
|
||||
|
||||
## Install
|
||||
|
||||
```
|
||||
npm install @actions/http-client --save
|
||||
```
|
||||
|
||||
## Samples
|
||||
|
||||
See the [HTTP](./__tests__) tests for detailed examples.
|
||||
|
||||
## Errors
|
||||
|
||||
### HTTP
|
||||
|
||||
The HTTP client does not throw unless truly exceptional.
|
||||
|
||||
* A request that successfully executes resulting in a 404, 500 etc... will return a response object with a status code and a body.
|
||||
* Redirects (3xx) will be followed by default.
|
||||
|
||||
See [HTTP tests](./__tests__) for detailed examples.
|
||||
|
||||
## Debugging
|
||||
|
||||
To enable detailed console logging of all HTTP requests and responses, set the NODE_DEBUG environment varible:
|
||||
|
||||
```
|
||||
export NODE_DEBUG=http
|
||||
```
|
||||
|
||||
## Node support
|
||||
|
||||
The http-client is built using the latest LTS version of Node 12. It may work on previous node LTS versions but it's tested and officially supported on Node12+.
|
||||
|
||||
## Support and Versioning
|
||||
|
||||
We follow semver and will hold compatibility between major versions and increment the minor version with new features and capabilities (while holding compat).
|
||||
|
||||
## Contributing
|
||||
|
||||
We welcome PRs. Please create an issue and if applicable, a design before proceeding with code.
|
||||
|
||||
once:
|
||||
|
||||
```bash
|
||||
$ npm install
|
||||
```
|
||||
|
||||
To build:
|
||||
|
||||
```bash
|
||||
$ npm run build
|
||||
```
|
||||
|
||||
To run all tests:
|
||||
```bash
|
||||
$ npm test
|
||||
```
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
## Releases
|
||||
|
||||
## 1.0.10
|
||||
|
||||
Contains a bug fix where proxy is defined without a user and password. see [PR here](https://github.com/actions/http-client/pull/42)
|
||||
|
||||
## 1.0.9
|
||||
Throw HttpClientError instead of a generic Error from the \<verb>Json() helper methods when the server responds with a non-successful status code.
|
||||
|
||||
## 1.0.8
|
||||
Fixed security issue where a redirect (e.g. 302) to another domain would pass headers. The fix was to strip the authorization header if the hostname was different. More [details in PR #27](https://github.com/actions/http-client/pull/27)
|
||||
|
||||
## 1.0.7
|
||||
Update NPM dependencies and add 429 to the list of HttpCodes
|
||||
|
||||
## 1.0.6
|
||||
Automatically sends Content-Type and Accept application/json headers for \<verb>Json() helper methods if not set in the client or parameters.
|
||||
|
||||
## 1.0.5
|
||||
Adds \<verb>Json() helper methods for json over http scenarios.
|
||||
|
||||
## 1.0.4
|
||||
Started to add \<verb>Json() helper methods. Do not use this release for that. Use >= 1.0.5 since there was an issue with types.
|
||||
|
||||
## 1.0.1 to 1.0.3
|
||||
Adds proxy support.
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user