Compare commits

...

13 Commits

Author SHA1 Message Date
Koushik Dey c8fbd76ede Sending a warning instead of throwing error when KUBECONFIG is missing. (#141) 2021-07-27 18:17:13 +05:30
Koushik Dey ec2a943cdd Removing optional input arguments from the action (#139) 2021-06-24 11:46:30 +05:30
Jyotsna 9a12be5d3d Users/jysin/pm feedback changes for v1.3 (#98)
* Changed dockerfile, manifest, helmchart links and README
2021-01-07 12:53:01 +05:30
Jyotsna d4a7d27ca4 Bug fix for dockerfile path v1.3 (#95)
* Bug fix for dockerfile path
2020-12-30 22:18:13 +05:30
Jyotsna 5920a5b1b9 Adding annotation new changes to v1.3 (#83)
* Adding new traceability fields to annotations
2020-12-29 17:03:52 +05:30
Ajinkya 9793dbc8aa Fixing issues found in bug bash. (#73) 2020-11-02 11:09:45 +05:30
Koushik Dey e1ddefa019 Merge pull request #65 from Azure/users/koushdey/workflowAnnotations-1.3
Annotation and Label changes to handle multiple workflows across branches
2020-09-22 13:42:38 +05:30
Koushik Dey a4d28e7b97 Updated package and node modules 2020-09-21 18:25:10 +05:30
Koushik Dey 5902071a20 Annotation and Label changes to handle multiple workflows across branches(master) 2020-09-21 17:24:17 +05:30
Ajinkya 23de7006f4 Update node_modules 2020-09-18 12:52:14 +05:30
Ajinkya 47741599e5 Freeze packages to latest available versions 2020-09-18 12:51:11 +05:30
Ajinkya 68ed252e91 Committing changes in js files 2020-09-17 13:02:22 +05:30
Ajinkya 309ae69061 Adding node_modules 2020-09-17 13:00:55 +05:30
270 changed files with 46224 additions and 2602 deletions
+1 -1
View File
@@ -218,7 +218,7 @@ Following are the key capabilities of this action:
service.yaml service.yaml
ingress-yml ingress-yml
strategy: blue-green strategy: blue-green
strategy: ingress # should be the same as the value when action was deploy route-method: ingress # should be the same as the value when action was deploy
action: promote # substitute reject if you want to reject action: promote # substitute reject if you want to reject
``` ```
+84 -16
View File
@@ -7,10 +7,12 @@ import * as fs from 'fs';
import * as io from '@actions/io'; import * as io from '@actions/io';
import * as toolCache from '@actions/tool-cache'; import * as toolCache from '@actions/tool-cache';
import * as fileHelper from '../src/utilities/files-helper'; import * as fileHelper from '../src/utilities/files-helper';
import { workflowAnnotations } from '../src/constants'; import { getWorkflowAnnotationKeyLabel, getWorkflowAnnotationsJson } from '../src/constants';
import * as inputParam from '../src/input-parameters'; import * as inputParam from '../src/input-parameters';
import { Kubectl, Resource } from '../src/kubectl-object-model'; import { Kubectl, Resource } from '../src/kubectl-object-model';
import * as httpClient from '../src/utilities/httpClient';
import * as utility from '../src/utilities/utility';
import { getkubectlDownloadURL } from "../src/utilities/kubectl-util"; import { getkubectlDownloadURL } from "../src/utilities/kubectl-util";
import { mocked } from 'ts-jest/utils'; import { mocked } from 'ts-jest/utils';
@@ -36,13 +38,47 @@ const getAllPodsMock = {
const getNamespaceMock = { const getNamespaceMock = {
'code': 0, 'code': 0,
'stdout': '{"apiVersion": "v1","kind": "Namespace","metadata": {"annotations": {"workflow": ".github/workflows/workflow.yml","runUri": "https://github.com/testRepo/actions/runs/12345"}},"spec": {"finalizers": ["kubernetes"]},"status": {"phase": "Active"}}' 'stdout': '{"apiVersion": "v1","kind": "Namespace","metadata": {"annotations": {"githubWorkflow_c11401b9d232942bac19cbc5bc32b42d": "{\'run\': \'202489005\',\'repository\': \'testUser/hello-kubernetes\',\'workflow\': \'workflow1\',\'jobName\': \'build-and-deploy\',\'createdBy\': \'testUser\',\'runUri\': \'https://github.com/testUser/hello-kubernetes/actions/runs/202489005\',\'commit\': \'currentCommit\',\'lastSuccessRunCommit\': \'lastCommit\',\'branch\': \'refs/heads/branch-rename\',\'deployTimestamp\': \'1597062957973\',\'dockerfilePaths\': \'{}\',\'manifestsPaths\': \'[]\',\'helmChartPaths\': \'[]\',\'provider\': \'GitHub\'}","githubWorkflow_21fd7a597282ca5adc05ba99018b3706": "{\'run\': \'202504411\',\'repository\': \'testUser/hello-kubernetes\',\'workflow\': \'workflowMaster\',\'jobName\': \'build-and-deploy\',\'createdBy\': \'testUser\',\'runUri\': \'https://github.com/testUser/hello-kubernetes/actions/runs/202504411\',\'commit\': \'currentCommit1\',\'lastSuccessRunCommit\': \'NA\',\'branch\': \'refs/heads/master\',\'deployTimestamp\': \'1597063919873\',\'dockerfilePaths\': \'{}\',\'manifestsPaths\': \'[]\',\'helmChartPaths\': \'[]\',\'provider\': \'GitHub\'}"}},"spec": {"finalizers": ["kubernetes"]},"status": {"phase": "Active"}}'
}; };
const getWorkflowsUrlResponse = {
'statusCode': httpClient.StatusCodes.OK,
'body': {
"total_count": 2,
"workflows": [
{
"id": 1477727,
"node_id": "MDg6V29ya2Zsb3cxNDYwNzI3",
"name": ".github/workflows/workflow.yml",
"path": ".github/workflows/workflow.yml",
"state": "active",
"created_at": "2020-06-03T23:41:06.000+05:30",
"updated_at": "2020-08-07T15:46:42.000+05:30",
"url": "https://api.github.com/repos/testUser/hello-kubernetes/actions/workflows/1460727",
"html_url": "https://github.com/testUser/hello-kubernetes/blob/master/.github/workflows/workflow.yml",
"badge_url": "https://github.com/testUser/hello-kubernetes/workflows/.github/workflows/workflow.yml/badge.svg"
},
{
"id": 1532230,
"node_id": "MDg6V29ya2Zsb3cxNTMyMzMw",
"name": "NewWorkflow",
"path": ".github/workflows/workflow1.yml",
"state": "active",
"created_at": "2020-06-11T16:05:23.000+05:30",
"updated_at": "2020-08-07T15:46:42.000+05:30",
"url": "https://api.github.com/repos/testUser/hello-kubernetes/actions/workflows/1532330",
"html_url": "https://github.com/testUser/hello-kubernetes/blob/master/.github/workflows/workflowNew.yml",
"badge_url": "https://github.com/testUser/hello-kubernetes/workflows/KoDeyi/badge.svg"
}
]
}
} as httpClient.WebResponse;
const resources: Resource[] = [{ type: "Deployment", name: "AppName" }]; const resources: Resource[] = [{ type: "Deployment", name: "AppName" }];
beforeEach(() => { beforeEach(() => {
deploymentYaml = fs.readFileSync(path.join(__dirname, 'manifests', 'deployment.yml'), 'utf8'); deploymentYaml = fs.readFileSync(path.join(__dirname, 'manifests', 'deployment.yml'), 'utf8');
jest.spyOn(Date, 'now').mockImplementation(() => 1234561234567);
process.env["KUBECONFIG"] = 'kubeConfig'; process.env["KUBECONFIG"] = 'kubeConfig';
process.env['GITHUB_RUN_ID'] = '12345'; process.env['GITHUB_RUN_ID'] = '12345';
@@ -52,6 +88,7 @@ beforeEach(() => {
process.env['GITHUB_REPOSITORY'] = 'testRepo'; process.env['GITHUB_REPOSITORY'] = 'testRepo';
process.env['GITHUB_SHA'] = 'testCommit'; process.env['GITHUB_SHA'] = 'testCommit';
process.env['GITHUB_REF'] = 'testBranch'; process.env['GITHUB_REF'] = 'testBranch';
process.env['GITHUB_TOKEN'] = 'testToken';
}) })
test("setKubectlPath() - install a particular version", async () => { test("setKubectlPath() - install a particular version", async () => {
@@ -213,9 +250,11 @@ test("deployment - deploy() - Invokes with manifestfiles", async () => {
kubeCtl.describe = jest.fn().mockReturnValue(""); kubeCtl.describe = jest.fn().mockReturnValue("");
kubeCtl.annotateFiles = jest.fn().mockReturnValue(""); kubeCtl.annotateFiles = jest.fn().mockReturnValue("");
kubeCtl.annotate = jest.fn().mockReturnValue(""); kubeCtl.annotate = jest.fn().mockReturnValue("");
kubeCtl.labelFiles = jest.fn().mockReturnValue("");
KubernetesManifestUtilityMock.checkManifestStability = jest.fn().mockReturnValue(""); KubernetesManifestUtilityMock.checkManifestStability = jest.fn().mockReturnValue("");
const readFileSpy = jest.spyOn(fs, 'readFileSync').mockImplementation(() => deploymentYaml); const readFileSpy = jest.spyOn(fs, 'readFileSync').mockImplementation(() => deploymentYaml);
jest.spyOn(httpClient, 'sendRequest').mockImplementation(() => Promise.resolve(getWorkflowsUrlResponse));
//Invoke and assert //Invoke and assert
await expect(deployment.deploy(kubeCtl, ['manifests/deployment.yaml'], undefined)).resolves.not.toThrowError(); await expect(deployment.deploy(kubeCtl, ['manifests/deployment.yaml'], undefined)).resolves.not.toThrowError();
@@ -241,9 +280,11 @@ test("deployment - deploy() - deploy force flag on", async () => {
kubeCtl.describe = jest.fn().mockReturnValue(""); kubeCtl.describe = jest.fn().mockReturnValue("");
kubeCtl.annotateFiles = jest.fn().mockReturnValue(""); kubeCtl.annotateFiles = jest.fn().mockReturnValue("");
kubeCtl.annotate = jest.fn().mockReturnValue(""); kubeCtl.annotate = jest.fn().mockReturnValue("");
kubeCtl.labelFiles = jest.fn().mockReturnValue("");
KubernetesManifestUtilityMock.checkManifestStability = jest.fn().mockReturnValue(""); KubernetesManifestUtilityMock.checkManifestStability = jest.fn().mockReturnValue("");
const deploySpy = jest.spyOn(kubeCtl, 'apply').mockImplementation(() => applyResMock); const deploySpy = jest.spyOn(kubeCtl, 'apply').mockImplementation(() => applyResMock);
jest.spyOn(httpClient, 'sendRequest').mockImplementation(() => Promise.resolve(getWorkflowsUrlResponse));
//Invoke and assert //Invoke and assert
await expect(deployment.deploy(kubeCtl, ['manifests/deployment.yaml'], undefined)).resolves.not.toThrowError(); await expect(deployment.deploy(kubeCtl, ['manifests/deployment.yaml'], undefined)).resolves.not.toThrowError();
@@ -251,15 +292,20 @@ test("deployment - deploy() - deploy force flag on", async () => {
deploySpy.mockRestore(); deploySpy.mockRestore();
}); });
test("deployment - deploy() - Annotate resources", async () => { test("deployment - deploy() - Annotate & label resources", async () => {
var deploymentConfig: utility.DeploymentConfig = { manifestFilePaths :['manifests/deployment.yaml'], helmChartFilePaths : [], dockerfilePaths : {} } ;
let annotationKeyValStr = getWorkflowAnnotationKeyLabel(process.env.GITHUB_WORKFLOW) + '=' + getWorkflowAnnotationsJson('currentCommit', '.github/workflows/workflow.yml', deploymentConfig);
const KubernetesManifestUtilityMock = mocked(KubernetesManifestUtility, true); const KubernetesManifestUtilityMock = mocked(KubernetesManifestUtility, true);
KubernetesManifestUtilityMock.checkManifestStability = jest.fn().mockReturnValue(""); KubernetesManifestUtilityMock.checkManifestStability = jest.fn().mockReturnValue("");
const KubernetesObjectUtilityMock = mocked(KubernetesObjectUtility, true); const KubernetesObjectUtilityMock = mocked(KubernetesObjectUtility, true);
KubernetesObjectUtilityMock.getResources = jest.fn().mockReturnValue(resources); KubernetesObjectUtilityMock.getResources = jest.fn().mockReturnValue(resources);
const fileHelperMock = mocked(fileHelper, true); const fileHelperMock = mocked(fileHelper, true);
const fsMock = (mocked(fs, true)); const fsMock = (mocked(fs, true));
fileHelperMock.getTempDirectory = jest.fn().mockReturnValue("Local/Temp/"); fileHelperMock.getTempDirectory = jest.fn().mockReturnValue("~/Deployment_testapp_currentTimestamp");
fsMock.writeFileSync =jest.fn().mockReturnValue(""); fsMock.writeFileSync =jest.fn().mockReturnValue("");
jest.spyOn(utility, 'getWorkflowFilePath').mockImplementation(() => Promise.resolve(process.env.GITHUB_WORKFLOW));
jest.spyOn(utility, 'getDeploymentConfig').mockImplementation(()=> Promise.resolve(deploymentConfig));
const kubeCtl: jest.Mocked<Kubectl> = new Kubectl("") as any; const kubeCtl: jest.Mocked<Kubectl> = new Kubectl("") as any;
kubeCtl.apply = jest.fn().mockReturnValue(""); kubeCtl.apply = jest.fn().mockReturnValue("");
kubeCtl.getResource = jest.fn().mockReturnValue(getNamespaceMock); kubeCtl.getResource = jest.fn().mockReturnValue(getNamespaceMock);
@@ -267,23 +313,31 @@ test("deployment - deploy() - Annotate resources", async () => {
kubeCtl.getNewReplicaSet = jest.fn().mockReturnValue("testpod-776cbc86f9"); kubeCtl.getNewReplicaSet = jest.fn().mockReturnValue("testpod-776cbc86f9");
kubeCtl.annotateFiles = jest.fn().mockReturnValue(""); kubeCtl.annotateFiles = jest.fn().mockReturnValue("");
kubeCtl.annotate = jest.fn().mockReturnValue(""); kubeCtl.annotate = jest.fn().mockReturnValue("");
kubeCtl.labelFiles = jest.fn();
//Invoke and assert //Invoke and assert
await expect(deployment.deploy(kubeCtl, ['manifests/deployment.yaml'], undefined)).resolves.not.toThrowError(); await expect(deployment.deploy(kubeCtl, ['manifests/deployment.yaml'], undefined)).resolves.not.toThrowError();
expect(kubeCtl.annotateFiles).toBeCalledWith(["Local/Temp/deployment.yaml"], workflowAnnotations, true); expect(kubeCtl.annotate).toHaveBeenNthCalledWith(1, 'namespace', 'default', annotationKeyValStr);
expect(kubeCtl.annotateFiles).toBeCalledWith(["~/Deployment_testapp_currentTimestamp/deployment.yaml"], annotationKeyValStr);
expect(kubeCtl.annotate).toBeCalledTimes(2); expect(kubeCtl.annotate).toBeCalledTimes(2);
expect(kubeCtl.labelFiles).toBeCalledWith(["~/Deployment_testapp_currentTimestamp/deployment.yaml"],
[`workflowFriendlyName=workflow.yml`, `workflow=${getWorkflowAnnotationKeyLabel(process.env.GITHUB_WORKFLOW)}`]);
}); });
test("deployment - deploy() - Skip Annotate namespace", async () => { test("deployment - deploy() - Annotate & label resources for a new workflow", async () => {
process.env['GITHUB_REPOSITORY'] = 'test1Repo'; process.env.GITHUB_WORKFLOW = '.github/workflows/NewWorkflow.yml';
var deploymentConfig: utility.DeploymentConfig = { manifestFilePaths :['manifests/deployment.yaml'], helmChartFilePaths : [], dockerfilePaths : {} } ;
let annotationKeyValStr = getWorkflowAnnotationKeyLabel(process.env.GITHUB_WORKFLOW) + '=' + getWorkflowAnnotationsJson('NA', '.github/workflows/NewWorkflow.yml', deploymentConfig);
const KubernetesManifestUtilityMock = mocked(KubernetesManifestUtility, true); const KubernetesManifestUtilityMock = mocked(KubernetesManifestUtility, true);
KubernetesManifestUtilityMock.checkManifestStability = jest.fn().mockReturnValue(""); KubernetesManifestUtilityMock.checkManifestStability = jest.fn().mockReturnValue("");
const KubernetesObjectUtilityMock = mocked(KubernetesObjectUtility, true); const KubernetesObjectUtilityMock = mocked(KubernetesObjectUtility, true);
KubernetesObjectUtilityMock.getResources = jest.fn().mockReturnValue(resources); KubernetesObjectUtilityMock.getResources = jest.fn().mockReturnValue(resources);
const fileHelperMock = mocked(fileHelper, true); const fileHelperMock = mocked(fileHelper, true);
const fsMock = (mocked(fs, true)); const fsMock = (mocked(fs, true));
fileHelperMock.getTempDirectory = jest.fn().mockReturnValue("Local/Temp/"); fileHelperMock.getTempDirectory = jest.fn().mockReturnValue("~/Deployment_testapp_currentTimestamp");
fsMock.writeFileSync =jest.fn().mockReturnValue(""); fsMock.writeFileSync =jest.fn().mockReturnValue("");
jest.spyOn(httpClient, 'sendRequest').mockImplementation(() => Promise.resolve(getWorkflowsUrlResponse));
jest.spyOn(utility, 'getDeploymentConfig').mockImplementation(()=> Promise.resolve(deploymentConfig));
const kubeCtl: jest.Mocked<Kubectl> = new Kubectl("") as any; const kubeCtl: jest.Mocked<Kubectl> = new Kubectl("") as any;
kubeCtl.apply = jest.fn().mockReturnValue(""); kubeCtl.apply = jest.fn().mockReturnValue("");
kubeCtl.getResource = jest.fn().mockReturnValue(getNamespaceMock); kubeCtl.getResource = jest.fn().mockReturnValue(getNamespaceMock);
@@ -291,14 +345,14 @@ test("deployment - deploy() - Skip Annotate namespace", async () => {
kubeCtl.getNewReplicaSet = jest.fn().mockReturnValue("testpod-776cbc86f9"); kubeCtl.getNewReplicaSet = jest.fn().mockReturnValue("testpod-776cbc86f9");
kubeCtl.annotateFiles = jest.fn().mockReturnValue(""); kubeCtl.annotateFiles = jest.fn().mockReturnValue("");
kubeCtl.annotate = jest.fn().mockReturnValue(""); kubeCtl.annotate = jest.fn().mockReturnValue("");
kubeCtl.labelFiles = jest.fn();
const consoleOutputSpy = jest.spyOn(process.stdout, "write").mockImplementation();
//Invoke and assert //Invoke and assert
await expect(deployment.deploy(kubeCtl, ['manifests/deployment.yaml'], undefined)).resolves.not.toThrowError(); await expect(deployment.deploy(kubeCtl, ['manifests/deployment.yaml'], undefined)).resolves.not.toThrowError();
expect(kubeCtl.annotateFiles).toBeCalledWith(["Local/Temp/deployment.yaml"], workflowAnnotations, true); expect(kubeCtl.annotate).toHaveBeenNthCalledWith(1, 'namespace', 'default', annotationKeyValStr);
expect(kubeCtl.annotate).toBeCalledTimes(1); expect(kubeCtl.annotateFiles).toBeCalledWith(["~/Deployment_testapp_currentTimestamp/deployment.yaml"], annotationKeyValStr);
expect(consoleOutputSpy).toHaveBeenNthCalledWith(1, `##[debug]Skipping 'annotate namespace' as namespace annotated by other workflow` + os.EOL) expect(kubeCtl.annotate).toBeCalledTimes(2);
expect(kubeCtl.labelFiles).toBeCalledWith(["~/Deployment_testapp_currentTimestamp/deployment.yaml"],
[`workflowFriendlyName=NewWorkflow.yml`, `workflow=${getWorkflowAnnotationKeyLabel(process.env.GITHUB_WORKFLOW)}`]);
}); });
test("deployment - deploy() - Annotate resources failed", async () => { test("deployment - deploy() - Annotate resources failed", async () => {
@@ -320,10 +374,24 @@ test("deployment - deploy() - Annotate resources failed", async () => {
kubeCtl.describe = jest.fn().mockReturnValue(""); kubeCtl.describe = jest.fn().mockReturnValue("");
kubeCtl.annotateFiles = jest.fn().mockReturnValue(""); kubeCtl.annotateFiles = jest.fn().mockReturnValue("");
kubeCtl.annotate = jest.fn().mockReturnValue(annotateMock); kubeCtl.annotate = jest.fn().mockReturnValue(annotateMock);
kubeCtl.labelFiles = jest.fn().mockReturnValue("");
KubernetesManifestUtilityMock.checkManifestStability = jest.fn().mockReturnValue(""); KubernetesManifestUtilityMock.checkManifestStability = jest.fn().mockReturnValue("");
const consoleOutputSpy = jest.spyOn(process.stdout, "write").mockImplementation(); const consoleOutputSpy = jest.spyOn(process.stdout, "write").mockImplementation();
//Invoke and assert //Invoke and assert
await expect(deployment.deploy(kubeCtl, ['manifests/deployment.yaml'], undefined)).resolves.not.toThrowError(); await expect(deployment.deploy(kubeCtl, ['manifests/deployment.yaml'], undefined)).resolves.not.toThrowError();
expect(consoleOutputSpy).toHaveBeenNthCalledWith(1, '##[warning]kubectl annotate failed' + os.EOL) expect(consoleOutputSpy).toHaveBeenNthCalledWith(1, '::warning::kubectl annotate failed' + os.EOL)
});
test("utility - getWorkflowFilePath() - Get workflow file path under API failure", async () => {
//Mocks
const errorWebResponse = {
'statusCode': httpClient.StatusCodes.UNAUTHORIZED,
'body': {}
} as httpClient.WebResponse
jest.spyOn(httpClient, 'sendRequest').mockImplementation(() => Promise.resolve(errorWebResponse));
//Invoke and assert
await expect(utility.getWorkflowFilePath(process.env.GITHUB_TOKEN)).resolves.not.toThrowError;
await expect(utility.getWorkflowFilePath(process.env.GITHUB_TOKEN)).resolves.toBe(process.env.GITHUB_WORKFLOW);
}); });
+4 -3
View File
@@ -42,9 +42,6 @@ inputs:
description: 'Percentage of traffic redirect to canary deployment' description: 'Percentage of traffic redirect to canary deployment'
required: false required: false
default: 0 default: 0
args:
description: 'Arguments'
required: false
action: action:
description: 'deploy/promote/reject' description: 'deploy/promote/reject'
required: true required: true
@@ -53,6 +50,10 @@ inputs:
description: 'Deploy when a previous deployment already exists. If true then --force argument is added to the apply command' description: 'Deploy when a previous deployment already exists. If true then --force argument is added to the apply command'
required: false required: false
default: false default: false
token:
description: 'Github token'
default: ${{ github.token }}
required: true
branding: branding:
color: 'green' # optional, decorates the entry in the GitHub Marketplace color: 'green' # optional, decorates the entry in the GitHub Marketplace
+28 -13
View File
@@ -1,6 +1,6 @@
'use strict'; 'use strict';
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.workflowAnnotations = exports.workloadTypesWithRolloutStatus = exports.workloadTypes = exports.deploymentTypes = exports.ServiceTypes = exports.DiscoveryAndLoadBalancerResource = exports.KubernetesWorkload = void 0; exports.getWorkflowAnnotationKeyLabel = exports.getWorkflowAnnotationsJson = exports.workloadTypesWithRolloutStatus = exports.workloadTypes = exports.deploymentTypes = exports.ServiceTypes = exports.DiscoveryAndLoadBalancerResource = exports.KubernetesWorkload = void 0;
class KubernetesWorkload { class KubernetesWorkload {
} }
exports.KubernetesWorkload = KubernetesWorkload; exports.KubernetesWorkload = KubernetesWorkload;
@@ -25,15 +25,30 @@ ServiceTypes.clusterIP = 'ClusterIP';
exports.deploymentTypes = ['deployment', 'replicaset', 'daemonset', 'pod', 'statefulset']; exports.deploymentTypes = ['deployment', 'replicaset', 'daemonset', 'pod', 'statefulset'];
exports.workloadTypes = ['deployment', 'replicaset', 'daemonset', 'pod', 'statefulset', 'job', 'cronjob']; exports.workloadTypes = ['deployment', 'replicaset', 'daemonset', 'pod', 'statefulset', 'job', 'cronjob'];
exports.workloadTypesWithRolloutStatus = ['deployment', 'daemonset', 'statefulset']; exports.workloadTypesWithRolloutStatus = ['deployment', 'daemonset', 'statefulset'];
exports.workflowAnnotations = [ function getWorkflowAnnotationsJson(lastSuccessRunSha, workflowFilePath, deploymentConfig) {
`run=${process.env['GITHUB_RUN_ID']}`, let annotationObject = {};
`repository=${process.env['GITHUB_REPOSITORY']}`, annotationObject["run"] = process.env.GITHUB_RUN_ID;
`workflow=${process.env['GITHUB_WORKFLOW']}`, annotationObject["repository"] = process.env.GITHUB_REPOSITORY;
`jobName=${process.env['GITHUB_JOB']}`, annotationObject["workflow"] = process.env.GITHUB_WORKFLOW;
`createdBy=${process.env['GITHUB_ACTOR']}`, annotationObject["workflowFileName"] = workflowFilePath.replace(".github/workflows/", "");
`runUri=https://github.com/${process.env['GITHUB_REPOSITORY']}/actions/runs/${process.env['GITHUB_RUN_ID']}`, annotationObject["jobName"] = process.env.GITHUB_JOB;
`commit=${process.env['GITHUB_SHA']}`, annotationObject["createdBy"] = process.env.GITHUB_ACTOR;
`branch=${process.env['GITHUB_REF']}`, annotationObject["runUri"] = `https://github.com/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`;
`deployTimestamp=${Date.now()}`, annotationObject["commit"] = process.env.GITHUB_SHA;
`provider=GitHub` annotationObject["lastSuccessRunCommit"] = lastSuccessRunSha;
]; annotationObject["branch"] = process.env.GITHUB_REF;
annotationObject["deployTimestamp"] = Date.now();
annotationObject["dockerfilePaths"] = deploymentConfig.dockerfilePaths;
annotationObject["manifestsPaths"] = deploymentConfig.manifestFilePaths;
annotationObject["helmChartPaths"] = deploymentConfig.helmChartFilePaths;
annotationObject["provider"] = "GitHub";
return JSON.stringify(annotationObject);
}
exports.getWorkflowAnnotationsJson = getWorkflowAnnotationsJson;
function getWorkflowAnnotationKeyLabel(workflowFilePath) {
const hashKey = require("crypto").createHash("MD5")
.update(`${process.env.GITHUB_REPOSITORY}/${workflowFilePath}`)
.digest("hex");
return `githubWorkflow_${hashKey}`;
}
exports.getWorkflowAnnotationKeyLabel = getWorkflowAnnotationKeyLabel;
+31
View File
@@ -0,0 +1,31 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DockerExec = void 0;
const tool_runner_1 = require("./utilities/tool-runner");
class DockerExec {
constructor(dockerPath) {
this.dockerPath = dockerPath;
}
;
pull(image, args, silent) {
args = ['pull', image, ...args];
let result = this.execute(args, silent);
if (result.stderr != '' && result.code != 0) {
throw new Error(`docker images pull failed with: ${result.error}`);
}
}
inspect(image, args, silent) {
args = ['inspect', image, ...args];
let result = this.execute(args, silent);
if (result.stderr != '' && result.code != 0) {
throw new Error(`docker inspect call failed with: ${result.error}`);
}
return result.stdout;
}
execute(args, silent) {
const command = new tool_runner_1.ToolRunner(this.dockerPath);
command.arg(args);
return command.execSync({ silent: !!silent });
}
}
exports.DockerExec = DockerExec;
+35
View File
@@ -0,0 +1,35 @@
"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 });
exports.GitHubClient = void 0;
const core = require("@actions/core");
const httpClient_1 = require("./utilities/httpClient");
class GitHubClient {
constructor(repository, token) {
this._repository = repository;
this._token = token;
}
getWorkflows() {
return __awaiter(this, void 0, void 0, function* () {
const getWorkflowFileNameUrl = `https://api.github.com/repos/${this._repository}/actions/workflows`;
const webRequest = new httpClient_1.WebRequest();
webRequest.method = "GET";
webRequest.uri = getWorkflowFileNameUrl;
webRequest.headers = {
Authorization: `Bearer ${this._token}`
};
core.debug(`Getting workflows for repo: ${this._repository}`);
const response = yield httpClient_1.sendRequest(webRequest);
return Promise.resolve(response);
});
}
}
exports.GitHubClient = GitHubClient;
+5 -2
View File
@@ -1,6 +1,6 @@
'use strict'; 'use strict';
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.forceDeployment = exports.args = exports.baselineAndCanaryReplicas = exports.versionSwitchBuffer = exports.routeMethod = exports.trafficSplitMethod = exports.deploymentStrategy = exports.canaryPercentage = exports.manifests = exports.imagePullSecrets = exports.containers = exports.namespace = void 0; exports.githubToken = exports.forceDeployment = exports.baselineAndCanaryReplicas = exports.versionSwitchBuffer = exports.routeMethod = exports.trafficSplitMethod = exports.deploymentStrategy = exports.canaryPercentage = exports.manifests = exports.imagePullSecrets = exports.containers = exports.namespace = void 0;
const core = require("@actions/core"); const core = require("@actions/core");
exports.namespace = core.getInput('namespace'); exports.namespace = core.getInput('namespace');
exports.containers = core.getInput('images').split('\n'); exports.containers = core.getInput('images').split('\n');
@@ -12,12 +12,15 @@ exports.trafficSplitMethod = core.getInput('traffic-split-method');
exports.routeMethod = core.getInput('route-method'); exports.routeMethod = core.getInput('route-method');
exports.versionSwitchBuffer = core.getInput('version-switch-buffer'); exports.versionSwitchBuffer = core.getInput('version-switch-buffer');
exports.baselineAndCanaryReplicas = core.getInput('baseline-and-canary-replicas'); exports.baselineAndCanaryReplicas = core.getInput('baseline-and-canary-replicas');
exports.args = core.getInput('arguments');
exports.forceDeployment = core.getInput('force').toLowerCase() == 'true'; exports.forceDeployment = core.getInput('force').toLowerCase() == 'true';
exports.githubToken = core.getInput("token");
if (!exports.namespace) { if (!exports.namespace) {
core.debug('Namespace was not supplied; using "default" namespace instead.'); core.debug('Namespace was not supplied; using "default" namespace instead.');
exports.namespace = 'default'; exports.namespace = 'default';
} }
if (!exports.githubToken) {
core.error("'token' input is not supplied. Set it to a PAT/GITHUB_TOKEN");
}
try { try {
const pe = parseInt(exports.canaryPercentage); const pe = parseInt(exports.canaryPercentage);
if (pe < 0 || pe > 100) { if (pe < 0 || pe > 100) {
+13 -10
View File
@@ -37,21 +37,24 @@ class Kubectl {
} }
return newReplicaSet; return newReplicaSet;
} }
annotate(resourceType, resourceName, annotations, overwrite) { annotate(resourceType, resourceName, annotation) {
let args = ['annotate', resourceType, resourceName]; let args = ['annotate', resourceType, resourceName];
args = args.concat(annotations); args.push(annotation);
if (!!overwrite) { args.push(`--overwrite`);
args.push(`--overwrite`);
}
return this.execute(args); return this.execute(args);
} }
annotateFiles(files, annotations, overwrite) { annotateFiles(files, annotation) {
let args = ['annotate']; let args = ['annotate'];
args = args.concat(['-f', this.createInlineArray(files)]); args = args.concat(['-f', this.createInlineArray(files)]);
args = args.concat(annotations); args.push(annotation);
if (!!overwrite) { args.push(`--overwrite`);
args.push(`--overwrite`); return this.execute(args);
} }
labelFiles(files, labels) {
let args = ['label'];
args = args.concat(['-f', this.createInlineArray(files)]);
args = args.concat(labels);
args.push(`--overwrite`);
return this.execute(args); return this.execute(args);
} }
getAllPods() { getAllPods() {
+1 -1
View File
@@ -53,7 +53,7 @@ function installKubectl(version) {
} }
function checkClusterContext() { function checkClusterContext() {
if (!process.env["KUBECONFIG"]) { if (!process.env["KUBECONFIG"]) {
throw new Error('Cluster context not set. Use k8ssetcontext action to set cluster context'); core.warning('KUBECONFIG env is not explicitly set. Ensure cluster context is set by using k8s-set-context / aks-set-context action.');
} }
} }
function run() { function run() {
+111
View File
@@ -0,0 +1,111 @@
"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 });
exports.sleepFor = exports.sendRequest = exports.WebRequestOptions = exports.WebResponse = exports.WebRequest = exports.StatusCodes = void 0;
// Taken from https://github.com/Azure/aks-set-context/blob/master/src/client.ts
const util = require("util");
const fs = require("fs");
const httpClient = require("typed-rest-client/HttpClient");
const core = require("@actions/core");
var httpCallbackClient = new httpClient.HttpClient('GITHUB_RUNNER', null, {});
var StatusCodes;
(function (StatusCodes) {
StatusCodes[StatusCodes["OK"] = 200] = "OK";
StatusCodes[StatusCodes["CREATED"] = 201] = "CREATED";
StatusCodes[StatusCodes["ACCEPTED"] = 202] = "ACCEPTED";
StatusCodes[StatusCodes["UNAUTHORIZED"] = 401] = "UNAUTHORIZED";
StatusCodes[StatusCodes["NOT_FOUND"] = 404] = "NOT_FOUND";
StatusCodes[StatusCodes["INTERNAL_SERVER_ERROR"] = 500] = "INTERNAL_SERVER_ERROR";
StatusCodes[StatusCodes["SERVICE_UNAVAILABLE"] = 503] = "SERVICE_UNAVAILABLE";
})(StatusCodes = exports.StatusCodes || (exports.StatusCodes = {}));
class WebRequest {
}
exports.WebRequest = WebRequest;
class WebResponse {
}
exports.WebResponse = WebResponse;
class WebRequestOptions {
}
exports.WebRequestOptions = WebRequestOptions;
function sendRequest(request, options) {
return __awaiter(this, void 0, void 0, function* () {
let i = 0;
let retryCount = options && options.retryCount ? options.retryCount : 5;
let retryIntervalInSeconds = options && options.retryIntervalInSeconds ? options.retryIntervalInSeconds : 2;
let retriableErrorCodes = options && options.retriableErrorCodes ? options.retriableErrorCodes : ["ETIMEDOUT", "ECONNRESET", "ENOTFOUND", "ESOCKETTIMEDOUT", "ECONNREFUSED", "EHOSTUNREACH", "EPIPE", "EA_AGAIN"];
let retriableStatusCodes = options && options.retriableStatusCodes ? options.retriableStatusCodes : [408, 409, 500, 502, 503, 504];
let timeToWait = retryIntervalInSeconds;
while (true) {
try {
if (request.body && typeof (request.body) !== 'string' && !request.body["readable"]) {
request.body = fs.createReadStream(request.body["path"]);
}
let response = yield sendRequestInternal(request);
if (retriableStatusCodes.indexOf(response.statusCode) != -1 && ++i < retryCount) {
core.debug(util.format("Encountered a retriable status code: %s. Message: '%s'.", response.statusCode, response.statusMessage));
yield sleepFor(timeToWait);
timeToWait = timeToWait * retryIntervalInSeconds + retryIntervalInSeconds;
continue;
}
return response;
}
catch (error) {
if (retriableErrorCodes.indexOf(error.code) != -1 && ++i < retryCount) {
core.debug(util.format("Encountered a retriable error:%s. Message: %s.", error.code, error.message));
yield sleepFor(timeToWait);
timeToWait = timeToWait * retryIntervalInSeconds + retryIntervalInSeconds;
}
else {
if (error.code) {
core.debug("error code =" + error.code);
}
throw error;
}
}
}
});
}
exports.sendRequest = sendRequest;
function sleepFor(sleepDurationInSeconds) {
return new Promise((resolve, reject) => {
setTimeout(resolve, sleepDurationInSeconds * 1000);
});
}
exports.sleepFor = sleepFor;
function sendRequestInternal(request) {
return __awaiter(this, void 0, void 0, function* () {
core.debug(util.format("[%s]%s", request.method, request.uri));
var response = yield httpCallbackClient.request(request.method, request.uri, request.body, request.headers);
return yield toWebResponse(response);
});
}
function toWebResponse(response) {
return __awaiter(this, void 0, void 0, function* () {
var res = new WebResponse();
if (response) {
res.statusCode = response.message.statusCode;
res.statusMessage = response.message.statusMessage;
res.headers = response.message.headers;
var body = yield response.readBody();
if (body) {
try {
res.body = JSON.parse(body);
}
catch (error) {
core.debug("Could not parse response: " + JSON.stringify(error));
core.debug("Response: " + JSON.stringify(res.body));
res.body = body;
}
}
}
return res;
});
}
@@ -55,7 +55,7 @@ function deploy(kubectl, manifestFilePaths, deploymentStrategy) {
catch (e) { catch (e) {
core.debug("Unable to parse pods; Error: " + e); core.debug("Unable to parse pods; Error: " + e);
} }
annotateResources(deployedManifestFiles, kubectl, resourceTypes, allPods); annotateAndLabelResources(deployedManifestFiles, kubectl, resourceTypes, allPods);
}); });
} }
exports.deploy = deploy; exports.deploy = deploy;
@@ -131,18 +131,36 @@ function checkManifestStability(kubectl, resources) {
yield KubernetesManifestUtility.checkManifestStability(kubectl, resources); yield KubernetesManifestUtility.checkManifestStability(kubectl, resources);
}); });
} }
function annotateResources(files, kubectl, resourceTypes, allPods) { function annotateAndLabelResources(files, kubectl, resourceTypes, allPods) {
return __awaiter(this, void 0, void 0, function* () {
const workflowFilePath = yield utility_1.getWorkflowFilePath(TaskInputParameters.githubToken);
const deploymentConfig = yield utility_1.getDeploymentConfig();
const annotationKeyLabel = models.getWorkflowAnnotationKeyLabel(workflowFilePath);
annotateResources(files, kubectl, resourceTypes, allPods, annotationKeyLabel, workflowFilePath, deploymentConfig);
labelResources(files, kubectl, annotationKeyLabel);
});
}
function annotateResources(files, kubectl, resourceTypes, allPods, annotationKey, workflowFilePath, deploymentConfig) {
const annotateResults = []; const annotateResults = [];
annotateResults.push(utility_1.annotateNamespace(kubectl, TaskInputParameters.namespace)); const lastSuccessSha = utility_1.getLastSuccessfulRunSha(kubectl, TaskInputParameters.namespace, annotationKey);
annotateResults.push(kubectl.annotateFiles(files, models.workflowAnnotations, true)); let annotationKeyValStr = annotationKey + '=' + models.getWorkflowAnnotationsJson(lastSuccessSha, workflowFilePath, deploymentConfig);
annotateResults.push(kubectl.annotate('namespace', TaskInputParameters.namespace, annotationKeyValStr));
annotateResults.push(kubectl.annotateFiles(files, annotationKeyValStr));
resourceTypes.forEach(resource => { resourceTypes.forEach(resource => {
if (resource.type.toUpperCase() !== models.KubernetesWorkload.pod.toUpperCase()) { if (resource.type.toUpperCase() !== models.KubernetesWorkload.pod.toUpperCase()) {
utility_1.annotateChildPods(kubectl, resource.type, resource.name, allPods) utility_1.annotateChildPods(kubectl, resource.type, resource.name, annotationKeyValStr, allPods)
.forEach(execResult => annotateResults.push(execResult)); .forEach(execResult => annotateResults.push(execResult));
} }
}); });
utility_1.checkForErrors(annotateResults, true); utility_1.checkForErrors(annotateResults, true);
} }
function labelResources(files, kubectl, label) {
let workflowName = process.env.GITHUB_WORKFLOW;
workflowName = workflowName.startsWith('.github/workflows/') ?
workflowName.replace(".github/workflows/", "") : workflowName;
const labels = [`workflowFriendlyName=${workflowName}`, `workflow=${label}`];
utility_1.checkForErrors([kubectl.labelFiles(files, labels)], true);
}
function isCanaryDeploymentStrategy(deploymentStrategy) { function isCanaryDeploymentStrategy(deploymentStrategy) {
return deploymentStrategy != null && deploymentStrategy.toUpperCase() === canaryDeploymentHelper.CANARY_DEPLOYMENT_STRATEGY.toUpperCase(); return deploymentStrategy != null && deploymentStrategy.toUpperCase() === canaryDeploymentHelper.CANARY_DEPLOYMENT_STRATEGY.toUpperCase();
} }
@@ -71,7 +71,7 @@ exports.rejectBlueGreenIngress = rejectBlueGreenIngress;
function routeBlueGreenIngress(kubectl, nextLabel, serviceNameMap, ingressEntityList) { function routeBlueGreenIngress(kubectl, nextLabel, serviceNameMap, ingressEntityList) {
let newObjectsList = []; let newObjectsList = [];
if (!nextLabel) { if (!nextLabel) {
newObjectsList = newObjectsList.concat(ingressEntityList); newObjectsList = ingressEntityList.filter(ingress => isIngressRouted(ingress, serviceNameMap));
} }
else { else {
ingressEntityList.forEach((inputObject) => { ingressEntityList.forEach((inputObject) => {
@@ -174,9 +174,9 @@ exports.validateTrafficSplitsState = validateTrafficSplitsState;
function cleanupSMI(kubectl, serviceEntityList) { function cleanupSMI(kubectl, serviceEntityList) {
const deleteList = []; const deleteList = [];
serviceEntityList.forEach((serviceObject) => { serviceEntityList.forEach((serviceObject) => {
deleteList.push({ name: blue_green_helper_1.getBlueGreenResourceName(serviceObject.metadata.name, TRAFFIC_SPLIT_OBJECT_NAME_SUFFIX), kind: TRAFFIC_SPLIT_OBJECT });
deleteList.push({ name: blue_green_helper_1.getBlueGreenResourceName(serviceObject.metadata.name, blue_green_helper_2.GREEN_SUFFIX), kind: serviceObject.kind }); deleteList.push({ name: blue_green_helper_1.getBlueGreenResourceName(serviceObject.metadata.name, blue_green_helper_2.GREEN_SUFFIX), kind: serviceObject.kind });
deleteList.push({ name: blue_green_helper_1.getBlueGreenResourceName(serviceObject.metadata.name, blue_green_helper_2.STABLE_SUFFIX), kind: serviceObject.kind }); deleteList.push({ name: blue_green_helper_1.getBlueGreenResourceName(serviceObject.metadata.name, blue_green_helper_2.STABLE_SUFFIX), kind: serviceObject.kind });
deleteList.push({ name: blue_green_helper_1.getBlueGreenResourceName(serviceObject.metadata.name, TRAFFIC_SPLIT_OBJECT_NAME_SUFFIX), kind: TRAFFIC_SPLIT_OBJECT });
}); });
// deleting all objects // deleting all objects
blue_green_helper_1.deleteObjects(kubectl, deleteList); blue_green_helper_1.deleteObjects(kubectl, deleteList);
+136 -22
View File
@@ -1,9 +1,22 @@
"use strict"; "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 }); Object.defineProperty(exports, "__esModule", { value: true });
exports.getCurrentTime = exports.getRandomInt = exports.sleep = exports.annotateNamespace = exports.annotateChildPods = exports.checkForErrors = exports.isEqual = exports.getExecutableExtension = void 0; exports.getNormalizedPath = exports.isHttpUrl = exports.getCurrentTime = exports.getRandomInt = exports.sleep = exports.getDeploymentConfig = exports.annotateChildPods = exports.getWorkflowFilePath = exports.getLastSuccessfulRunSha = exports.checkForErrors = exports.isEqual = exports.getExecutableExtension = void 0;
const os = require("os"); const os = require("os");
const core = require("@actions/core"); const core = require("@actions/core");
const constants_1 = require("../constants"); const githubClient_1 = require("../githubClient");
const httpClient_1 = require("./httpClient");
const inputParams = require("../input-parameters");
const docker_object_model_1 = require("../docker-object-model");
const io = require("@actions/io");
function getExecutableExtension() { function getExecutableExtension() {
if (os.type().match(/^Win/)) { if (os.type().match(/^Win/)) {
return '.exe'; return '.exe';
@@ -50,7 +63,63 @@ function checkForErrors(execResults, warnIfError) {
} }
} }
exports.checkForErrors = checkForErrors; exports.checkForErrors = checkForErrors;
function annotateChildPods(kubectl, resourceType, resourceName, allPods) { function getLastSuccessfulRunSha(kubectl, namespaceName, annotationKey) {
try {
const result = kubectl.getResource('namespace', namespaceName);
if (result) {
if (result.stderr) {
core.warning(`${result.stderr}`);
return process.env.GITHUB_SHA;
}
else if (result.stdout) {
const annotationsSet = JSON.parse(result.stdout).metadata.annotations;
if (annotationsSet && annotationsSet[annotationKey]) {
return JSON.parse(annotationsSet[annotationKey].replace(/'/g, '"')).commit;
}
else {
return 'NA';
}
}
}
}
catch (ex) {
core.warning(`Failed to get commits from cluster. ${JSON.stringify(ex)}`);
return '';
}
}
exports.getLastSuccessfulRunSha = getLastSuccessfulRunSha;
function getWorkflowFilePath(githubToken) {
return __awaiter(this, void 0, void 0, function* () {
let workflowFilePath = process.env.GITHUB_WORKFLOW;
if (!workflowFilePath.startsWith('.github/workflows/')) {
const githubClient = new githubClient_1.GitHubClient(process.env.GITHUB_REPOSITORY, githubToken);
const response = yield githubClient.getWorkflows();
if (response) {
if (response.statusCode == httpClient_1.StatusCodes.OK
&& response.body
&& response.body.total_count) {
if (response.body.total_count > 0) {
for (let workflow of response.body.workflows) {
if (process.env.GITHUB_WORKFLOW === workflow.name) {
workflowFilePath = workflow.path;
break;
}
}
}
}
else if (response.statusCode != httpClient_1.StatusCodes.OK) {
core.debug(`An error occured while getting list of workflows on the repo. Statuscode: ${response.statusCode}, StatusMessage: ${response.statusMessage}`);
}
}
else {
core.warning(`Failed to get response from workflow list API`);
}
}
return Promise.resolve(workflowFilePath);
});
}
exports.getWorkflowFilePath = getWorkflowFilePath;
function annotateChildPods(kubectl, resourceType, resourceName, annotationKeyValStr, allPods) {
const commandExecutionResults = []; const commandExecutionResults = [];
let owner = resourceName; let owner = resourceName;
if (resourceType.toLowerCase().indexOf('deployment') > -1) { if (resourceType.toLowerCase().indexOf('deployment') > -1) {
@@ -62,7 +131,7 @@ function annotateChildPods(kubectl, resourceType, resourceName, allPods) {
if (owners) { if (owners) {
for (let ownerRef of owners) { for (let ownerRef of owners) {
if (ownerRef.name === owner) { if (ownerRef.name === owner) {
commandExecutionResults.push(kubectl.annotate('pod', pod.metadata.name, constants_1.workflowAnnotations, true)); commandExecutionResults.push(kubectl.annotate('pod', pod.metadata.name, annotationKeyValStr));
break; break;
} }
} }
@@ -72,28 +141,34 @@ function annotateChildPods(kubectl, resourceType, resourceName, allPods) {
return commandExecutionResults; return commandExecutionResults;
} }
exports.annotateChildPods = annotateChildPods; exports.annotateChildPods = annotateChildPods;
function annotateNamespace(kubectl, namespaceName) { function getDeploymentConfig() {
const result = kubectl.getResource('namespace', namespaceName); return __awaiter(this, void 0, void 0, function* () {
if (!result) { let helmChartPaths = (process.env.HELM_CHART_PATHS && process.env.HELM_CHART_PATHS.split(';').filter(path => path != "")) || [];
return { code: 1, stderr: 'Failed to get resource' }; helmChartPaths = helmChartPaths.map(helmchart => getNormalizedPath(helmchart.trim()));
} let inputManifestFiles = inputParams.manifests || [];
else { if (!helmChartPaths.length) {
if (result.stderr) { inputManifestFiles = inputManifestFiles.map(manifestFile => getNormalizedPath(manifestFile));
return result;
} }
else if (result.stdout) { const imageNames = inputParams.containers || [];
const annotationsSet = JSON.parse(result.stdout).metadata.annotations; let imageDockerfilePathMap = {};
if (annotationsSet && annotationsSet.runUri) { //Fetching from image label if available
if (annotationsSet.runUri.indexOf(process.env['GITHUB_REPOSITORY']) == -1) { for (const image of imageNames) {
core.debug(`Skipping 'annotate namespace' as namespace annotated by other workflow`); try {
return { code: 0, stdout: '' }; imageDockerfilePathMap[image] = yield getDockerfilePath(image);
} }
catch (ex) {
core.warning(`Failed to get dockerfile path for image ${image.toString()} | ` + ex);
} }
return kubectl.annotate('namespace', namespaceName, constants_1.workflowAnnotations, true);
} }
} const deploymentConfig = {
manifestFilePaths: inputManifestFiles,
helmChartFilePaths: helmChartPaths,
dockerfilePaths: imageDockerfilePathMap
};
return Promise.resolve(deploymentConfig);
});
} }
exports.annotateNamespace = annotateNamespace; exports.getDeploymentConfig = getDeploymentConfig;
function sleep(timeout) { function sleep(timeout) {
return new Promise(resolve => setTimeout(resolve, timeout)); return new Promise(resolve => setTimeout(resolve, timeout));
} }
@@ -106,3 +181,42 @@ function getCurrentTime() {
return new Date().getTime(); return new Date().getTime();
} }
exports.getCurrentTime = getCurrentTime; exports.getCurrentTime = getCurrentTime;
function checkDockerPath() {
return __awaiter(this, void 0, void 0, function* () {
let dockerPath = yield io.which('docker', false);
if (!dockerPath) {
throw new Error('Docker is not installed.');
}
});
}
function getDockerfilePath(image) {
return __awaiter(this, void 0, void 0, function* () {
let imageConfig, imageInspectResult;
var dockerExec = new docker_object_model_1.DockerExec('docker');
yield checkDockerPath();
dockerExec.pull(image, [], true);
imageInspectResult = dockerExec.inspect(image, [], true);
imageConfig = JSON.parse(imageInspectResult)[0];
const DOCKERFILE_PATH_LABEL_KEY = 'dockerfile-path';
let pathValue = '';
if (imageConfig) {
if ((imageConfig.Config) && (imageConfig.Config.Labels) && (imageConfig.Config.Labels[DOCKERFILE_PATH_LABEL_KEY])) {
const pathLabel = imageConfig.Config.Labels[DOCKERFILE_PATH_LABEL_KEY];
pathValue = getNormalizedPath(pathLabel);
}
}
return pathValue;
});
}
function isHttpUrl(url) {
const HTTP_REGEX = /^https?:\/\/.*$/;
return HTTP_REGEX.test(url);
}
exports.isHttpUrl = isHttpUrl;
function getNormalizedPath(pathValue) {
if (!isHttpUrl(pathValue)) { //if it is not an http url then convert to link from current repo and commit
return `https://github.com/${process.env.GITHUB_REPOSITORY}/blob/${process.env.GITHUB_SHA}/${pathValue}`;
}
return pathValue;
}
exports.getNormalizedPath = getNormalizedPath;
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../esprima/bin/esparse.js
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../esprima/bin/esvalidate.js
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../js-yaml/bin/js-yaml.js
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../semver/bin/semver.js
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../uuid/bin/uuid
+9
View File
@@ -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.
+147
View File
@@ -0,0 +1,147 @@
# `@actions/core`
> Core functions for setting results, logging, registering secrets and exporting variables across actions
## Usage
### Import the package
```js
// javascript
const core = require('@actions/core');
// typescript
import * as core from '@actions/core';
```
#### Inputs/Outputs
Action inputs can be read with `getInput`. Outputs can be set with `setOutput` which makes them available to be mapped into inputs of other actions to ensure they are decoupled.
```js
const myInput = core.getInput('inputName', { required: true });
core.setOutput('outputKey', 'outputVal');
```
#### Exporting variables
Since each step runs in a separate process, you can use `exportVariable` to add it to this step and future steps environment blocks.
```js
core.exportVariable('envVar', 'Val');
```
#### Setting a secret
Setting a secret registers the secret with the runner to ensure it is masked in logs.
```js
core.setSecret('myPassword');
```
#### PATH Manipulation
To make a tool's path available in the path for the remainder of the job (without altering the machine or containers state), use `addPath`. The runner will prepend the path given to the jobs PATH.
```js
core.addPath('/path/to/mytool');
```
#### Exit codes
You should use this library to set the failing exit code for your action. If status is not set and the script runs to completion, that will lead to a success.
```js
const core = require('@actions/core');
try {
// Do stuff
}
catch (err) {
// setFailed logs the message and sets a failing exit code
core.setFailed(`Action failed with error ${err}`);
}
Note that `setNeutral` is not yet implemented in actions V2 but equivalent functionality is being planned.
```
#### Logging
Finally, this library provides some utilities for logging. Note that debug logging is hidden from the logs by default. This behavior can be toggled by enabling the [Step Debug Logs](../../docs/action-debugging.md#step-debug-logs).
```js
const core = require('@actions/core');
const myInput = core.getInput('input');
try {
core.debug('Inside try block');
if (!myInput) {
core.warning('myInput was not set');
}
if (core.isDebug()) {
// curl -v https://github.com
} else {
// curl https://github.com
}
// Do stuff
core.info('Output to the actions build log')
}
catch (err) {
core.error(`Error ${err}, action may still succeed though`);
}
```
This library can also wrap chunks of output in foldable groups.
```js
const core = require('@actions/core')
// Manually wrap output
core.startGroup('Do some function')
doSomeFunction()
core.endGroup()
// Wrap an asynchronous function call
const result = await core.group('Do something async', async () => {
const response = await doSomeHTTPRequest()
return response
})
```
#### Action state
You can use this library to save state and get state for sharing information between a given wrapper action:
**action.yml**
```yaml
name: 'Wrapper action sample'
inputs:
name:
default: 'GitHub'
runs:
using: 'node12'
main: 'main.js'
post: 'cleanup.js'
```
In action's `main.js`:
```js
const core = require('@actions/core');
core.saveState("pidToKill", 12345);
```
In action's `cleanup.js`:
```js
const core = require('@actions/core');
var pid = core.getState("pidToKill");
process.kill(pid);
```
+21
View File
@@ -0,0 +1,21 @@
interface CommandProperties {
[key: string]: any;
}
/**
* Commands
*
* Command Format:
* ::name key=value,key=value::message
*
* Examples:
* ::warning::This is the message
* ::set-env name=MY_VAR::some value
*/
export declare function issueCommand(command: string, properties: CommandProperties, message: any): void;
export declare function issue(name: string, message?: string): void;
/**
* Sanitizes an input into a string so it can be passed into issueCommand safely
* @param input input to sanitize into a string
*/
export declare function toCommandValue(input: any): string;
export {};
+92
View File
@@ -0,0 +1,92 @@
"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 os = __importStar(require("os"));
/**
* Commands
*
* Command Format:
* ::name key=value,key=value::message
*
* Examples:
* ::warning::This is the message
* ::set-env name=MY_VAR::some value
*/
function issueCommand(command, properties, message) {
const cmd = new Command(command, properties, message);
process.stdout.write(cmd.toString() + os.EOL);
}
exports.issueCommand = issueCommand;
function issue(name, message = '') {
issueCommand(name, {}, message);
}
exports.issue = issue;
const CMD_STRING = '::';
class Command {
constructor(command, properties, message) {
if (!command) {
command = 'missing.command';
}
this.command = command;
this.properties = properties;
this.message = message;
}
toString() {
let cmdStr = CMD_STRING + this.command;
if (this.properties && Object.keys(this.properties).length > 0) {
cmdStr += ' ';
let first = true;
for (const key in this.properties) {
if (this.properties.hasOwnProperty(key)) {
const val = this.properties[key];
if (val) {
if (first) {
first = false;
}
else {
cmdStr += ',';
}
cmdStr += `${key}=${escapeProperty(val)}`;
}
}
}
}
cmdStr += `${CMD_STRING}${escapeData(this.message)}`;
return cmdStr;
}
}
/**
* Sanitizes an input into a string so it can be passed into issueCommand safely
* @param input input to sanitize into a string
*/
function toCommandValue(input) {
if (input === null || input === undefined) {
return '';
}
else if (typeof input === 'string' || input instanceof String) {
return input;
}
return JSON.stringify(input);
}
exports.toCommandValue = toCommandValue;
function escapeData(s) {
return toCommandValue(s)
.replace(/%/g, '%25')
.replace(/\r/g, '%0D')
.replace(/\n/g, '%0A');
}
function escapeProperty(s) {
return toCommandValue(s)
.replace(/%/g, '%25')
.replace(/\r/g, '%0D')
.replace(/\n/g, '%0A')
.replace(/:/g, '%3A')
.replace(/,/g, '%2C');
}
//# sourceMappingURL=command.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"command.js","sourceRoot":"","sources":["../src/command.ts"],"names":[],"mappings":";;;;;;;;;AAAA,uCAAwB;AAWxB;;;;;;;;;GASG;AACH,SAAgB,YAAY,CAC1B,OAAe,EACf,UAA6B,EAC7B,OAAY;IAEZ,MAAM,GAAG,GAAG,IAAI,OAAO,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,CAAA;IACrD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAA;AAC/C,CAAC;AAPD,oCAOC;AAED,SAAgB,KAAK,CAAC,IAAY,EAAE,UAAkB,EAAE;IACtD,YAAY,CAAC,IAAI,EAAE,EAAE,EAAE,OAAO,CAAC,CAAA;AACjC,CAAC;AAFD,sBAEC;AAED,MAAM,UAAU,GAAG,IAAI,CAAA;AAEvB,MAAM,OAAO;IAKX,YAAY,OAAe,EAAE,UAA6B,EAAE,OAAe;QACzE,IAAI,CAAC,OAAO,EAAE;YACZ,OAAO,GAAG,iBAAiB,CAAA;SAC5B;QAED,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;QACtB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;QAC5B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;IACxB,CAAC;IAED,QAAQ;QACN,IAAI,MAAM,GAAG,UAAU,GAAG,IAAI,CAAC,OAAO,CAAA;QAEtC,IAAI,IAAI,CAAC,UAAU,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE;YAC9D,MAAM,IAAI,GAAG,CAAA;YACb,IAAI,KAAK,GAAG,IAAI,CAAA;YAChB,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,UAAU,EAAE;gBACjC,IAAI,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE;oBACvC,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAA;oBAChC,IAAI,GAAG,EAAE;wBACP,IAAI,KAAK,EAAE;4BACT,KAAK,GAAG,KAAK,CAAA;yBACd;6BAAM;4BACL,MAAM,IAAI,GAAG,CAAA;yBACd;wBAED,MAAM,IAAI,GAAG,GAAG,IAAI,cAAc,CAAC,GAAG,CAAC,EAAE,CAAA;qBAC1C;iBACF;aACF;SACF;QAED,MAAM,IAAI,GAAG,UAAU,GAAG,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAA;QACpD,OAAO,MAAM,CAAA;IACf,CAAC;CACF;AAED;;;GAGG;AACH,SAAgB,cAAc,CAAC,KAAU;IACvC,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE;QACzC,OAAO,EAAE,CAAA;KACV;SAAM,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,YAAY,MAAM,EAAE;QAC/D,OAAO,KAAe,CAAA;KACvB;IACD,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;AAC9B,CAAC;AAPD,wCAOC;AAED,SAAS,UAAU,CAAC,CAAM;IACxB,OAAO,cAAc,CAAC,CAAC,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC;SACpB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;SACrB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;AAC1B,CAAC;AAED,SAAS,cAAc,CAAC,CAAM;IAC5B,OAAO,cAAc,CAAC,CAAC,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC;SACpB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;SACrB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC;SACpB,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;AACzB,CAAC"}
+122
View File
@@ -0,0 +1,122 @@
/**
* Interface for getInput options
*/
export interface InputOptions {
/** Optional. Whether the input is required. If required and not present, will throw. Defaults to false */
required?: boolean;
}
/**
* The code to exit an action
*/
export declare enum ExitCode {
/**
* A code indicating that the action was successful
*/
Success = 0,
/**
* A code indicating that the action was a failure
*/
Failure = 1
}
/**
* Sets env variable for this action and future actions in the job
* @param name the name of the variable to set
* @param val the value of the variable. Non-string values will be converted to a string via JSON.stringify
*/
export declare function exportVariable(name: string, val: any): void;
/**
* Registers a secret which will get masked from logs
* @param secret value of the secret
*/
export declare function setSecret(secret: string): void;
/**
* Prepends inputPath to the PATH (for this action and future actions)
* @param inputPath
*/
export declare function addPath(inputPath: string): void;
/**
* Gets the value of an input. The value is also trimmed.
*
* @param name name of the input to get
* @param options optional. See InputOptions.
* @returns string
*/
export declare function getInput(name: string, options?: InputOptions): string;
/**
* Sets the value of an output.
*
* @param name name of the output to set
* @param value value to store. Non-string values will be converted to a string via JSON.stringify
*/
export declare function setOutput(name: string, value: any): void;
/**
* Enables or disables the echoing of commands into stdout for the rest of the step.
* Echoing is disabled by default if ACTIONS_STEP_DEBUG is not set.
*
*/
export declare function setCommandEcho(enabled: boolean): void;
/**
* Sets the action status to failed.
* When the action exits it will be with an exit code of 1
* @param message add error issue message
*/
export declare function setFailed(message: string | Error): void;
/**
* Gets whether Actions Step Debug is on or not
*/
export declare function isDebug(): boolean;
/**
* Writes debug message to user log
* @param message debug message
*/
export declare function debug(message: string): void;
/**
* Adds an error issue
* @param message error issue message. Errors will be converted to string via toString()
*/
export declare function error(message: string | Error): void;
/**
* Adds an warning issue
* @param message warning issue message. Errors will be converted to string via toString()
*/
export declare function warning(message: string | Error): void;
/**
* Writes info to log with console.log.
* @param message info message
*/
export declare function info(message: string): void;
/**
* Begin an output group.
*
* Output until the next `groupEnd` will be foldable in this group
*
* @param name The name of the output group
*/
export declare function startGroup(name: string): void;
/**
* End an output group.
*/
export declare function endGroup(): void;
/**
* Wrap an asynchronous function call in a group.
*
* Returns the same type as the function itself.
*
* @param name The name of the group
* @param fn The function to wrap in the group
*/
export declare function group<T>(name: string, fn: () => Promise<T>): Promise<T>;
/**
* Saves state for current action, the state can only be retrieved by this action's post job execution.
*
* @param name name of the state to store
* @param value value to store. Non-string values will be converted to a string via JSON.stringify
*/
export declare function saveState(name: string, value: any): void;
/**
* Gets the value of an state set by this action's main execution.
*
* @param name name of the state to get
* @returns string
*/
export declare function getState(name: string): string;
+222
View File
@@ -0,0 +1,222 @@
"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 command_1 = require("./command");
const os = __importStar(require("os"));
const path = __importStar(require("path"));
/**
* The code to exit an action
*/
var ExitCode;
(function (ExitCode) {
/**
* A code indicating that the action was successful
*/
ExitCode[ExitCode["Success"] = 0] = "Success";
/**
* A code indicating that the action was a failure
*/
ExitCode[ExitCode["Failure"] = 1] = "Failure";
})(ExitCode = exports.ExitCode || (exports.ExitCode = {}));
//-----------------------------------------------------------------------
// Variables
//-----------------------------------------------------------------------
/**
* Sets env variable for this action and future actions in the job
* @param name the name of the variable to set
* @param val the value of the variable. Non-string values will be converted to a string via JSON.stringify
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function exportVariable(name, val) {
const convertedVal = command_1.toCommandValue(val);
process.env[name] = convertedVal;
command_1.issueCommand('set-env', { name }, convertedVal);
}
exports.exportVariable = exportVariable;
/**
* Registers a secret which will get masked from logs
* @param secret value of the secret
*/
function setSecret(secret) {
command_1.issueCommand('add-mask', {}, secret);
}
exports.setSecret = setSecret;
/**
* Prepends inputPath to the PATH (for this action and future actions)
* @param inputPath
*/
function addPath(inputPath) {
command_1.issueCommand('add-path', {}, inputPath);
process.env['PATH'] = `${inputPath}${path.delimiter}${process.env['PATH']}`;
}
exports.addPath = addPath;
/**
* Gets the value of an input. The value is also trimmed.
*
* @param name name of the input to get
* @param options optional. See InputOptions.
* @returns string
*/
function getInput(name, options) {
const val = process.env[`INPUT_${name.replace(/ /g, '_').toUpperCase()}`] || '';
if (options && options.required && !val) {
throw new Error(`Input required and not supplied: ${name}`);
}
return val.trim();
}
exports.getInput = getInput;
/**
* Sets the value of an output.
*
* @param name name of the output to set
* @param value value to store. Non-string values will be converted to a string via JSON.stringify
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function setOutput(name, value) {
command_1.issueCommand('set-output', { name }, value);
}
exports.setOutput = setOutput;
/**
* Enables or disables the echoing of commands into stdout for the rest of the step.
* Echoing is disabled by default if ACTIONS_STEP_DEBUG is not set.
*
*/
function setCommandEcho(enabled) {
command_1.issue('echo', enabled ? 'on' : 'off');
}
exports.setCommandEcho = setCommandEcho;
//-----------------------------------------------------------------------
// Results
//-----------------------------------------------------------------------
/**
* Sets the action status to failed.
* When the action exits it will be with an exit code of 1
* @param message add error issue message
*/
function setFailed(message) {
process.exitCode = ExitCode.Failure;
error(message);
}
exports.setFailed = setFailed;
//-----------------------------------------------------------------------
// Logging Commands
//-----------------------------------------------------------------------
/**
* Gets whether Actions Step Debug is on or not
*/
function isDebug() {
return process.env['RUNNER_DEBUG'] === '1';
}
exports.isDebug = isDebug;
/**
* Writes debug message to user log
* @param message debug message
*/
function debug(message) {
command_1.issueCommand('debug', {}, message);
}
exports.debug = debug;
/**
* Adds an error issue
* @param message error issue message. Errors will be converted to string via toString()
*/
function error(message) {
command_1.issue('error', message instanceof Error ? message.toString() : message);
}
exports.error = error;
/**
* Adds an warning issue
* @param message warning issue message. Errors will be converted to string via toString()
*/
function warning(message) {
command_1.issue('warning', message instanceof Error ? message.toString() : message);
}
exports.warning = warning;
/**
* Writes info to log with console.log.
* @param message info message
*/
function info(message) {
process.stdout.write(message + os.EOL);
}
exports.info = info;
/**
* Begin an output group.
*
* Output until the next `groupEnd` will be foldable in this group
*
* @param name The name of the output group
*/
function startGroup(name) {
command_1.issue('group', name);
}
exports.startGroup = startGroup;
/**
* End an output group.
*/
function endGroup() {
command_1.issue('endgroup');
}
exports.endGroup = endGroup;
/**
* Wrap an asynchronous function call in a group.
*
* Returns the same type as the function itself.
*
* @param name The name of the group
* @param fn The function to wrap in the group
*/
function group(name, fn) {
return __awaiter(this, void 0, void 0, function* () {
startGroup(name);
let result;
try {
result = yield fn();
}
finally {
endGroup();
}
return result;
});
}
exports.group = group;
//-----------------------------------------------------------------------
// Wrapper action state
//-----------------------------------------------------------------------
/**
* Saves state for current action, the state can only be retrieved by this action's post job execution.
*
* @param name name of the state to store
* @param value value to store. Non-string values will be converted to a string via JSON.stringify
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function saveState(name, value) {
command_1.issueCommand('save-state', { name }, value);
}
exports.saveState = saveState;
/**
* Gets the value of an state set by this action's main execution.
*
* @param name name of the state to get
* @returns string
*/
function getState(name) {
return process.env[`STATE_${name}`] || '';
}
exports.getState = getState;
//# sourceMappingURL=core.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"core.js","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAAA,uCAA6D;AAE7D,uCAAwB;AACxB,2CAA4B;AAU5B;;GAEG;AACH,IAAY,QAUX;AAVD,WAAY,QAAQ;IAClB;;OAEG;IACH,6CAAW,CAAA;IAEX;;OAEG;IACH,6CAAW,CAAA;AACb,CAAC,EAVW,QAAQ,GAAR,gBAAQ,KAAR,gBAAQ,QAUnB;AAED,yEAAyE;AACzE,YAAY;AACZ,yEAAyE;AAEzE;;;;GAIG;AACH,8DAA8D;AAC9D,SAAgB,cAAc,CAAC,IAAY,EAAE,GAAQ;IACnD,MAAM,YAAY,GAAG,wBAAc,CAAC,GAAG,CAAC,CAAA;IACxC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,YAAY,CAAA;IAChC,sBAAY,CAAC,SAAS,EAAE,EAAC,IAAI,EAAC,EAAE,YAAY,CAAC,CAAA;AAC/C,CAAC;AAJD,wCAIC;AAED;;;GAGG;AACH,SAAgB,SAAS,CAAC,MAAc;IACtC,sBAAY,CAAC,UAAU,EAAE,EAAE,EAAE,MAAM,CAAC,CAAA;AACtC,CAAC;AAFD,8BAEC;AAED;;;GAGG;AACH,SAAgB,OAAO,CAAC,SAAiB;IACvC,sBAAY,CAAC,UAAU,EAAE,EAAE,EAAE,SAAS,CAAC,CAAA;IACvC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,GAAG,SAAS,GAAG,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAA;AAC7E,CAAC;AAHD,0BAGC;AAED;;;;;;GAMG;AACH,SAAgB,QAAQ,CAAC,IAAY,EAAE,OAAsB;IAC3D,MAAM,GAAG,GACP,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,IAAI,EAAE,CAAA;IACrE,IAAI,OAAO,IAAI,OAAO,CAAC,QAAQ,IAAI,CAAC,GAAG,EAAE;QACvC,MAAM,IAAI,KAAK,CAAC,oCAAoC,IAAI,EAAE,CAAC,CAAA;KAC5D;IAED,OAAO,GAAG,CAAC,IAAI,EAAE,CAAA;AACnB,CAAC;AARD,4BAQC;AAED;;;;;GAKG;AACH,8DAA8D;AAC9D,SAAgB,SAAS,CAAC,IAAY,EAAE,KAAU;IAChD,sBAAY,CAAC,YAAY,EAAE,EAAC,IAAI,EAAC,EAAE,KAAK,CAAC,CAAA;AAC3C,CAAC;AAFD,8BAEC;AAED;;;;GAIG;AACH,SAAgB,cAAc,CAAC,OAAgB;IAC7C,eAAK,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;AACvC,CAAC;AAFD,wCAEC;AAED,yEAAyE;AACzE,UAAU;AACV,yEAAyE;AAEzE;;;;GAIG;AACH,SAAgB,SAAS,CAAC,OAAuB;IAC/C,OAAO,CAAC,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAA;IAEnC,KAAK,CAAC,OAAO,CAAC,CAAA;AAChB,CAAC;AAJD,8BAIC;AAED,yEAAyE;AACzE,mBAAmB;AACnB,yEAAyE;AAEzE;;GAEG;AACH,SAAgB,OAAO;IACrB,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,KAAK,GAAG,CAAA;AAC5C,CAAC;AAFD,0BAEC;AAED;;;GAGG;AACH,SAAgB,KAAK,CAAC,OAAe;IACnC,sBAAY,CAAC,OAAO,EAAE,EAAE,EAAE,OAAO,CAAC,CAAA;AACpC,CAAC;AAFD,sBAEC;AAED;;;GAGG;AACH,SAAgB,KAAK,CAAC,OAAuB;IAC3C,eAAK,CAAC,OAAO,EAAE,OAAO,YAAY,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAA;AACzE,CAAC;AAFD,sBAEC;AAED;;;GAGG;AACH,SAAgB,OAAO,CAAC,OAAuB;IAC7C,eAAK,CAAC,SAAS,EAAE,OAAO,YAAY,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAA;AAC3E,CAAC;AAFD,0BAEC;AAED;;;GAGG;AACH,SAAgB,IAAI,CAAC,OAAe;IAClC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,GAAG,CAAC,CAAA;AACxC,CAAC;AAFD,oBAEC;AAED;;;;;;GAMG;AACH,SAAgB,UAAU,CAAC,IAAY;IACrC,eAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;AACtB,CAAC;AAFD,gCAEC;AAED;;GAEG;AACH,SAAgB,QAAQ;IACtB,eAAK,CAAC,UAAU,CAAC,CAAA;AACnB,CAAC;AAFD,4BAEC;AAED;;;;;;;GAOG;AACH,SAAsB,KAAK,CAAI,IAAY,EAAE,EAAoB;;QAC/D,UAAU,CAAC,IAAI,CAAC,CAAA;QAEhB,IAAI,MAAS,CAAA;QAEb,IAAI;YACF,MAAM,GAAG,MAAM,EAAE,EAAE,CAAA;SACpB;gBAAS;YACR,QAAQ,EAAE,CAAA;SACX;QAED,OAAO,MAAM,CAAA;IACf,CAAC;CAAA;AAZD,sBAYC;AAED,yEAAyE;AACzE,uBAAuB;AACvB,yEAAyE;AAEzE;;;;;GAKG;AACH,8DAA8D;AAC9D,SAAgB,SAAS,CAAC,IAAY,EAAE,KAAU;IAChD,sBAAY,CAAC,YAAY,EAAE,EAAC,IAAI,EAAC,EAAE,KAAK,CAAC,CAAA;AAC3C,CAAC;AAFD,8BAEC;AAED;;;;;GAKG;AACH,SAAgB,QAAQ,CAAC,IAAY;IACnC,OAAO,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC,IAAI,EAAE,CAAA;AAC3C,CAAC;AAFD,4BAEC"}
+68
View File
@@ -0,0 +1,68 @@
{
"_from": "@actions/core@1.2.5",
"_id": "@actions/core@1.2.5",
"_inBundle": false,
"_integrity": "sha512-mwpoNjHSWWh0IiALdDEQi3tru124JKn0yVNziIBzTME8QRv7thwoghVuT1jBRjFvdtoHsqD58IRHy1nf86paRg==",
"_location": "/@actions/core",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "@actions/core@1.2.5",
"name": "@actions/core",
"escapedName": "@actions%2fcore",
"scope": "@actions",
"rawSpec": "1.2.5",
"saveSpec": null,
"fetchSpec": "1.2.5"
},
"_requiredBy": [
"/",
"/@actions/tool-cache"
],
"_resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.5.tgz",
"_shasum": "fa57bf8c07a38191e243beb9ea9d8368c1cb02c8",
"_spec": "@actions/core@1.2.5",
"_where": "/Users/aj/repos/k8s-deploy",
"bugs": {
"url": "https://github.com/actions/toolkit/issues"
},
"bundleDependencies": false,
"deprecated": false,
"description": "Actions core lib",
"devDependencies": {
"@types/node": "^12.0.2"
},
"directories": {
"lib": "lib",
"test": "__tests__"
},
"files": [
"lib",
"!.DS_Store"
],
"homepage": "https://github.com/actions/toolkit/tree/main/packages/core",
"keywords": [
"github",
"actions",
"core"
],
"license": "MIT",
"main": "lib/core.js",
"name": "@actions/core",
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "git+https://github.com/actions/toolkit.git",
"directory": "packages/core"
},
"scripts": {
"audit-moderate": "npm install && npm audit --json --audit-level=moderate > audit.json",
"test": "echo \"Error: run tests from root\" && exit 1",
"tsc": "tsc"
},
"types": "lib/core.d.ts",
"version": "1.2.5"
}
+57
View File
@@ -0,0 +1,57 @@
# `@actions/exec`
## Usage
#### Basic
You can use this package to execute tools in a cross platform way:
```js
const exec = require('@actions/exec');
await exec.exec('node index.js');
```
#### Args
You can also pass in arg arrays:
```js
const exec = require('@actions/exec');
await exec.exec('node', ['index.js', 'foo=bar']);
```
#### Output/options
Capture output or specify [other options](https://github.com/actions/toolkit/blob/d9347d4ab99fd507c0b9104b2cf79fb44fcc827d/packages/exec/src/interfaces.ts#L5):
```js
const exec = require('@actions/exec');
let myOutput = '';
let myError = '';
const options = {};
options.listeners = {
stdout: (data: Buffer) => {
myOutput += data.toString();
},
stderr: (data: Buffer) => {
myError += data.toString();
}
};
options.cwd = './lib';
await exec.exec('node', ['index.js', 'foo=bar'], options);
```
#### Exec tools not in the PATH
You can specify the full path for tools not in the PATH:
```js
const exec = require('@actions/exec');
await exec.exec('"/path/to/my-tool"', ['arg1']);
```
+13
View File
@@ -0,0 +1,13 @@
import { ExecOptions } from './interfaces';
export { ExecOptions };
/**
* Exec a command.
* Output will be streamed to the live console.
* Returns promise with return code
*
* @param commandLine command to execute (can include additional args). Must be correctly escaped.
* @param args optional arguments for tool. Escaping is handled by the lib.
* @param options optional exec options. See ExecOptions
* @returns Promise<number> exit code
*/
export declare function exec(commandLine: string, args?: string[], options?: ExecOptions): Promise<number>;
+44
View File
@@ -0,0 +1,44 @@
"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 tr = __importStar(require("./toolrunner"));
/**
* Exec a command.
* Output will be streamed to the live console.
* Returns promise with return code
*
* @param commandLine command to execute (can include additional args). Must be correctly escaped.
* @param args optional arguments for tool. Escaping is handled by the lib.
* @param options optional exec options. See ExecOptions
* @returns Promise<number> exit code
*/
function exec(commandLine, args, options) {
return __awaiter(this, void 0, void 0, function* () {
const commandArgs = tr.argStringToArray(commandLine);
if (commandArgs.length === 0) {
throw new Error(`Parameter 'commandLine' cannot be null or empty.`);
}
// Path to tool to execute should be first arg
const toolPath = commandArgs[0];
args = commandArgs.slice(1).concat(args || []);
const runner = new tr.ToolRunner(toolPath, args, options);
return runner.exec();
});
}
exports.exec = exec;
//# sourceMappingURL=exec.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"exec.js","sourceRoot":"","sources":["../src/exec.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AACA,iDAAkC;AAIlC;;;;;;;;;GASG;AACH,SAAsB,IAAI,CACxB,WAAmB,EACnB,IAAe,EACf,OAAqB;;QAErB,MAAM,WAAW,GAAG,EAAE,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAA;QACpD,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE;YAC5B,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAA;SACpE;QACD,8CAA8C;QAC9C,MAAM,QAAQ,GAAG,WAAW,CAAC,CAAC,CAAC,CAAA;QAC/B,IAAI,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,CAAA;QAC9C,MAAM,MAAM,GAAkB,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,CAAA;QACxE,OAAO,MAAM,CAAC,IAAI,EAAE,CAAA;IACtB,CAAC;CAAA;AAdD,oBAcC"}
+37
View File
@@ -0,0 +1,37 @@
/// <reference types="node" />
import * as stream from 'stream';
/**
* Interface for exec options
*/
export interface ExecOptions {
/** optional working directory. defaults to current */
cwd?: string;
/** optional envvar dictionary. defaults to current process's env */
env?: {
[key: string]: string;
};
/** optional. defaults to false */
silent?: boolean;
/** optional out stream to use. Defaults to process.stdout */
outStream?: stream.Writable;
/** optional err stream to use. Defaults to process.stderr */
errStream?: stream.Writable;
/** optional. whether to skip quoting/escaping arguments if needed. defaults to false. */
windowsVerbatimArguments?: boolean;
/** optional. whether to fail if output to stderr. defaults to false */
failOnStdErr?: boolean;
/** optional. defaults to failing on non zero. ignore will not fail leaving it up to the caller */
ignoreReturnCode?: boolean;
/** optional. How long in ms to wait for STDIO streams to close after the exit event of the process before terminating. defaults to 10000 */
delay?: number;
/** optional. input to write to the process on STDIN. */
input?: Buffer;
/** optional. Listeners for output. Callback functions that will be called on these events */
listeners?: {
stdout?: (data: Buffer) => void;
stderr?: (data: Buffer) => void;
stdline?: (data: string) => void;
errline?: (data: string) => void;
debug?: (data: string) => void;
};
}
+3
View File
@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=interfaces.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"interfaces.js","sourceRoot":"","sources":["../src/interfaces.ts"],"names":[],"mappings":""}
+37
View File
@@ -0,0 +1,37 @@
/// <reference types="node" />
import * as events from 'events';
import * as im from './interfaces';
export declare class ToolRunner extends events.EventEmitter {
constructor(toolPath: string, args?: string[], options?: im.ExecOptions);
private toolPath;
private args;
private options;
private _debug;
private _getCommandString;
private _processLineBuffer;
private _getSpawnFileName;
private _getSpawnArgs;
private _endsWith;
private _isCmdFile;
private _windowsQuoteCmdArg;
private _uvQuoteCmdArg;
private _cloneExecOptions;
private _getSpawnOptions;
/**
* Exec a tool.
* Output will be streamed to the live console.
* Returns promise with return code
*
* @param tool path to tool to exec
* @param options optional exec options. See ExecOptions
* @returns number
*/
exec(): Promise<number>;
}
/**
* Convert an arg string to an array of args. Handles escaping
*
* @param argString string of arguments
* @returns string[] array of arguments
*/
export declare function argStringToArray(argString: string): string[];
+600
View File
@@ -0,0 +1,600 @@
"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 os = __importStar(require("os"));
const events = __importStar(require("events"));
const child = __importStar(require("child_process"));
const path = __importStar(require("path"));
const io = __importStar(require("@actions/io"));
const ioUtil = __importStar(require("@actions/io/lib/io-util"));
/* eslint-disable @typescript-eslint/unbound-method */
const IS_WINDOWS = process.platform === 'win32';
/*
* Class for running command line tools. Handles quoting and arg parsing in a platform agnostic way.
*/
class ToolRunner extends events.EventEmitter {
constructor(toolPath, args, options) {
super();
if (!toolPath) {
throw new Error("Parameter 'toolPath' cannot be null or empty.");
}
this.toolPath = toolPath;
this.args = args || [];
this.options = options || {};
}
_debug(message) {
if (this.options.listeners && this.options.listeners.debug) {
this.options.listeners.debug(message);
}
}
_getCommandString(options, noPrefix) {
const toolPath = this._getSpawnFileName();
const args = this._getSpawnArgs(options);
let cmd = noPrefix ? '' : '[command]'; // omit prefix when piped to a second tool
if (IS_WINDOWS) {
// Windows + cmd file
if (this._isCmdFile()) {
cmd += toolPath;
for (const a of args) {
cmd += ` ${a}`;
}
}
// Windows + verbatim
else if (options.windowsVerbatimArguments) {
cmd += `"${toolPath}"`;
for (const a of args) {
cmd += ` ${a}`;
}
}
// Windows (regular)
else {
cmd += this._windowsQuoteCmdArg(toolPath);
for (const a of args) {
cmd += ` ${this._windowsQuoteCmdArg(a)}`;
}
}
}
else {
// OSX/Linux - this can likely be improved with some form of quoting.
// creating processes on Unix is fundamentally different than Windows.
// on Unix, execvp() takes an arg array.
cmd += toolPath;
for (const a of args) {
cmd += ` ${a}`;
}
}
return cmd;
}
_processLineBuffer(data, strBuffer, onLine) {
try {
let s = strBuffer + data.toString();
let n = s.indexOf(os.EOL);
while (n > -1) {
const line = s.substring(0, n);
onLine(line);
// the rest of the string ...
s = s.substring(n + os.EOL.length);
n = s.indexOf(os.EOL);
}
strBuffer = s;
}
catch (err) {
// streaming lines to console is best effort. Don't fail a build.
this._debug(`error processing line. Failed with error ${err}`);
}
}
_getSpawnFileName() {
if (IS_WINDOWS) {
if (this._isCmdFile()) {
return process.env['COMSPEC'] || 'cmd.exe';
}
}
return this.toolPath;
}
_getSpawnArgs(options) {
if (IS_WINDOWS) {
if (this._isCmdFile()) {
let argline = `/D /S /C "${this._windowsQuoteCmdArg(this.toolPath)}`;
for (const a of this.args) {
argline += ' ';
argline += options.windowsVerbatimArguments
? a
: this._windowsQuoteCmdArg(a);
}
argline += '"';
return [argline];
}
}
return this.args;
}
_endsWith(str, end) {
return str.endsWith(end);
}
_isCmdFile() {
const upperToolPath = this.toolPath.toUpperCase();
return (this._endsWith(upperToolPath, '.CMD') ||
this._endsWith(upperToolPath, '.BAT'));
}
_windowsQuoteCmdArg(arg) {
// for .exe, apply the normal quoting rules that libuv applies
if (!this._isCmdFile()) {
return this._uvQuoteCmdArg(arg);
}
// otherwise apply quoting rules specific to the cmd.exe command line parser.
// the libuv rules are generic and are not designed specifically for cmd.exe
// command line parser.
//
// for a detailed description of the cmd.exe command line parser, refer to
// http://stackoverflow.com/questions/4094699/how-does-the-windows-command-interpreter-cmd-exe-parse-scripts/7970912#7970912
// need quotes for empty arg
if (!arg) {
return '""';
}
// determine whether the arg needs to be quoted
const cmdSpecialChars = [
' ',
'\t',
'&',
'(',
')',
'[',
']',
'{',
'}',
'^',
'=',
';',
'!',
"'",
'+',
',',
'`',
'~',
'|',
'<',
'>',
'"'
];
let needsQuotes = false;
for (const char of arg) {
if (cmdSpecialChars.some(x => x === char)) {
needsQuotes = true;
break;
}
}
// short-circuit if quotes not needed
if (!needsQuotes) {
return arg;
}
// the following quoting rules are very similar to the rules that by libuv applies.
//
// 1) wrap the string in quotes
//
// 2) double-up quotes - i.e. " => ""
//
// this is different from the libuv quoting rules. libuv replaces " with \", which unfortunately
// doesn't work well with a cmd.exe command line.
//
// note, replacing " with "" also works well if the arg is passed to a downstream .NET console app.
// for example, the command line:
// foo.exe "myarg:""my val"""
// is parsed by a .NET console app into an arg array:
// [ "myarg:\"my val\"" ]
// which is the same end result when applying libuv quoting rules. although the actual
// command line from libuv quoting rules would look like:
// foo.exe "myarg:\"my val\""
//
// 3) double-up slashes that precede a quote,
// e.g. hello \world => "hello \world"
// hello\"world => "hello\\""world"
// hello\\"world => "hello\\\\""world"
// hello world\ => "hello world\\"
//
// technically this is not required for a cmd.exe command line, or the batch argument parser.
// the reasons for including this as a .cmd quoting rule are:
//
// a) this is optimized for the scenario where the argument is passed from the .cmd file to an
// external program. many programs (e.g. .NET console apps) rely on the slash-doubling rule.
//
// b) it's what we've been doing previously (by deferring to node default behavior) and we
// haven't heard any complaints about that aspect.
//
// note, a weakness of the quoting rules chosen here, is that % is not escaped. in fact, % cannot be
// escaped when used on the command line directly - even though within a .cmd file % can be escaped
// by using %%.
//
// the saving grace is, on the command line, %var% is left as-is if var is not defined. this contrasts
// the line parsing rules within a .cmd file, where if var is not defined it is replaced with nothing.
//
// one option that was explored was replacing % with ^% - i.e. %var% => ^%var^%. this hack would
// often work, since it is unlikely that var^ would exist, and the ^ character is removed when the
// variable is used. the problem, however, is that ^ is not removed when %* is used to pass the args
// to an external program.
//
// an unexplored potential solution for the % escaping problem, is to create a wrapper .cmd file.
// % can be escaped within a .cmd file.
let reverse = '"';
let quoteHit = true;
for (let i = arg.length; i > 0; i--) {
// walk the string in reverse
reverse += arg[i - 1];
if (quoteHit && arg[i - 1] === '\\') {
reverse += '\\'; // double the slash
}
else if (arg[i - 1] === '"') {
quoteHit = true;
reverse += '"'; // double the quote
}
else {
quoteHit = false;
}
}
reverse += '"';
return reverse
.split('')
.reverse()
.join('');
}
_uvQuoteCmdArg(arg) {
// Tool runner wraps child_process.spawn() and needs to apply the same quoting as
// Node in certain cases where the undocumented spawn option windowsVerbatimArguments
// is used.
//
// Since this function is a port of quote_cmd_arg from Node 4.x (technically, lib UV,
// see https://github.com/nodejs/node/blob/v4.x/deps/uv/src/win/process.c for details),
// pasting copyright notice from Node within this function:
//
// Copyright Joyent, Inc. and other Node contributors. All rights reserved.
//
// 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.
if (!arg) {
// Need double quotation for empty argument
return '""';
}
if (!arg.includes(' ') && !arg.includes('\t') && !arg.includes('"')) {
// No quotation needed
return arg;
}
if (!arg.includes('"') && !arg.includes('\\')) {
// No embedded double quotes or backslashes, so I can just wrap
// quote marks around the whole thing.
return `"${arg}"`;
}
// Expected input/output:
// input : hello"world
// output: "hello\"world"
// input : hello""world
// output: "hello\"\"world"
// input : hello\world
// output: hello\world
// input : hello\\world
// output: hello\\world
// input : hello\"world
// output: "hello\\\"world"
// input : hello\\"world
// output: "hello\\\\\"world"
// input : hello world\
// output: "hello world\\" - note the comment in libuv actually reads "hello world\"
// but it appears the comment is wrong, it should be "hello world\\"
let reverse = '"';
let quoteHit = true;
for (let i = arg.length; i > 0; i--) {
// walk the string in reverse
reverse += arg[i - 1];
if (quoteHit && arg[i - 1] === '\\') {
reverse += '\\';
}
else if (arg[i - 1] === '"') {
quoteHit = true;
reverse += '\\';
}
else {
quoteHit = false;
}
}
reverse += '"';
return reverse
.split('')
.reverse()
.join('');
}
_cloneExecOptions(options) {
options = options || {};
const result = {
cwd: options.cwd || process.cwd(),
env: options.env || process.env,
silent: options.silent || false,
windowsVerbatimArguments: options.windowsVerbatimArguments || false,
failOnStdErr: options.failOnStdErr || false,
ignoreReturnCode: options.ignoreReturnCode || false,
delay: options.delay || 10000
};
result.outStream = options.outStream || process.stdout;
result.errStream = options.errStream || process.stderr;
return result;
}
_getSpawnOptions(options, toolPath) {
options = options || {};
const result = {};
result.cwd = options.cwd;
result.env = options.env;
result['windowsVerbatimArguments'] =
options.windowsVerbatimArguments || this._isCmdFile();
if (options.windowsVerbatimArguments) {
result.argv0 = `"${toolPath}"`;
}
return result;
}
/**
* Exec a tool.
* Output will be streamed to the live console.
* Returns promise with return code
*
* @param tool path to tool to exec
* @param options optional exec options. See ExecOptions
* @returns number
*/
exec() {
return __awaiter(this, void 0, void 0, function* () {
// root the tool path if it is unrooted and contains relative pathing
if (!ioUtil.isRooted(this.toolPath) &&
(this.toolPath.includes('/') ||
(IS_WINDOWS && this.toolPath.includes('\\')))) {
// prefer options.cwd if it is specified, however options.cwd may also need to be rooted
this.toolPath = path.resolve(process.cwd(), this.options.cwd || process.cwd(), this.toolPath);
}
// if the tool is only a file name, then resolve it from the PATH
// otherwise verify it exists (add extension on Windows if necessary)
this.toolPath = yield io.which(this.toolPath, true);
return new Promise((resolve, reject) => {
this._debug(`exec tool: ${this.toolPath}`);
this._debug('arguments:');
for (const arg of this.args) {
this._debug(` ${arg}`);
}
const optionsNonNull = this._cloneExecOptions(this.options);
if (!optionsNonNull.silent && optionsNonNull.outStream) {
optionsNonNull.outStream.write(this._getCommandString(optionsNonNull) + os.EOL);
}
const state = new ExecState(optionsNonNull, this.toolPath);
state.on('debug', (message) => {
this._debug(message);
});
const fileName = this._getSpawnFileName();
const cp = child.spawn(fileName, this._getSpawnArgs(optionsNonNull), this._getSpawnOptions(this.options, fileName));
const stdbuffer = '';
if (cp.stdout) {
cp.stdout.on('data', (data) => {
if (this.options.listeners && this.options.listeners.stdout) {
this.options.listeners.stdout(data);
}
if (!optionsNonNull.silent && optionsNonNull.outStream) {
optionsNonNull.outStream.write(data);
}
this._processLineBuffer(data, stdbuffer, (line) => {
if (this.options.listeners && this.options.listeners.stdline) {
this.options.listeners.stdline(line);
}
});
});
}
const errbuffer = '';
if (cp.stderr) {
cp.stderr.on('data', (data) => {
state.processStderr = true;
if (this.options.listeners && this.options.listeners.stderr) {
this.options.listeners.stderr(data);
}
if (!optionsNonNull.silent &&
optionsNonNull.errStream &&
optionsNonNull.outStream) {
const s = optionsNonNull.failOnStdErr
? optionsNonNull.errStream
: optionsNonNull.outStream;
s.write(data);
}
this._processLineBuffer(data, errbuffer, (line) => {
if (this.options.listeners && this.options.listeners.errline) {
this.options.listeners.errline(line);
}
});
});
}
cp.on('error', (err) => {
state.processError = err.message;
state.processExited = true;
state.processClosed = true;
state.CheckComplete();
});
cp.on('exit', (code) => {
state.processExitCode = code;
state.processExited = true;
this._debug(`Exit code ${code} received from tool '${this.toolPath}'`);
state.CheckComplete();
});
cp.on('close', (code) => {
state.processExitCode = code;
state.processExited = true;
state.processClosed = true;
this._debug(`STDIO streams have closed for tool '${this.toolPath}'`);
state.CheckComplete();
});
state.on('done', (error, exitCode) => {
if (stdbuffer.length > 0) {
this.emit('stdline', stdbuffer);
}
if (errbuffer.length > 0) {
this.emit('errline', errbuffer);
}
cp.removeAllListeners();
if (error) {
reject(error);
}
else {
resolve(exitCode);
}
});
if (this.options.input) {
if (!cp.stdin) {
throw new Error('child process missing stdin');
}
cp.stdin.end(this.options.input);
}
});
});
}
}
exports.ToolRunner = ToolRunner;
/**
* Convert an arg string to an array of args. Handles escaping
*
* @param argString string of arguments
* @returns string[] array of arguments
*/
function argStringToArray(argString) {
const args = [];
let inQuotes = false;
let escaped = false;
let arg = '';
function append(c) {
// we only escape double quotes.
if (escaped && c !== '"') {
arg += '\\';
}
arg += c;
escaped = false;
}
for (let i = 0; i < argString.length; i++) {
const c = argString.charAt(i);
if (c === '"') {
if (!escaped) {
inQuotes = !inQuotes;
}
else {
append(c);
}
continue;
}
if (c === '\\' && escaped) {
append(c);
continue;
}
if (c === '\\' && inQuotes) {
escaped = true;
continue;
}
if (c === ' ' && !inQuotes) {
if (arg.length > 0) {
args.push(arg);
arg = '';
}
continue;
}
append(c);
}
if (arg.length > 0) {
args.push(arg.trim());
}
return args;
}
exports.argStringToArray = argStringToArray;
class ExecState extends events.EventEmitter {
constructor(options, toolPath) {
super();
this.processClosed = false; // tracks whether the process has exited and stdio is closed
this.processError = '';
this.processExitCode = 0;
this.processExited = false; // tracks whether the process has exited
this.processStderr = false; // tracks whether stderr was written to
this.delay = 10000; // 10 seconds
this.done = false;
this.timeout = null;
if (!toolPath) {
throw new Error('toolPath must not be empty');
}
this.options = options;
this.toolPath = toolPath;
if (options.delay) {
this.delay = options.delay;
}
}
CheckComplete() {
if (this.done) {
return;
}
if (this.processClosed) {
this._setResult();
}
else if (this.processExited) {
this.timeout = setTimeout(ExecState.HandleTimeout, this.delay, this);
}
}
_debug(message) {
this.emit('debug', message);
}
_setResult() {
// determine whether there is an error
let error;
if (this.processExited) {
if (this.processError) {
error = new Error(`There was an error when attempting to execute the process '${this.toolPath}'. This may indicate the process failed to start. Error: ${this.processError}`);
}
else if (this.processExitCode !== 0 && !this.options.ignoreReturnCode) {
error = new Error(`The process '${this.toolPath}' failed with exit code ${this.processExitCode}`);
}
else if (this.processStderr && this.options.failOnStdErr) {
error = new Error(`The process '${this.toolPath}' failed because one or more lines were written to the STDERR stream`);
}
}
// clear the timeout
if (this.timeout) {
clearTimeout(this.timeout);
this.timeout = null;
}
this.done = true;
this.emit('done', error, this.processExitCode);
}
static HandleTimeout(state) {
if (state.done) {
return;
}
if (!state.processClosed && state.processExited) {
const message = `The STDIO streams did not close within ${state.delay /
1000} seconds of the exit event from process '${state.toolPath}'. This may indicate a child process inherited the STDIO streams and has not yet exited.`;
state._debug(message);
}
state._setResult();
}
}
//# sourceMappingURL=toolrunner.js.map
File diff suppressed because one or more lines are too long
+67
View File
@@ -0,0 +1,67 @@
{
"_from": "@actions/exec@1.0.4",
"_id": "@actions/exec@1.0.4",
"_inBundle": false,
"_integrity": "sha512-4DPChWow9yc9W3WqEbUj8Nr86xkpyE29ZzWjXucHItclLbEW6jr80Zx4nqv18QL6KK65+cifiQZXvnqgTV6oHw==",
"_location": "/@actions/exec",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "@actions/exec@1.0.4",
"name": "@actions/exec",
"escapedName": "@actions%2fexec",
"scope": "@actions",
"rawSpec": "1.0.4",
"saveSpec": null,
"fetchSpec": "1.0.4"
},
"_requiredBy": [
"/",
"/@actions/tool-cache"
],
"_resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.0.4.tgz",
"_shasum": "99d75310e62e59fc37d2ee6dcff6d4bffadd3a5d",
"_spec": "@actions/exec@1.0.4",
"_where": "/Users/aj/repos/k8s-deploy",
"bugs": {
"url": "https://github.com/actions/toolkit/issues"
},
"bundleDependencies": false,
"dependencies": {
"@actions/io": "^1.0.1"
},
"deprecated": false,
"description": "Actions exec lib",
"directories": {
"lib": "lib",
"test": "__tests__"
},
"files": [
"lib"
],
"homepage": "https://github.com/actions/toolkit/tree/master/packages/exec",
"keywords": [
"github",
"actions",
"exec"
],
"license": "MIT",
"main": "lib/exec.js",
"name": "@actions/exec",
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "git+https://github.com/actions/toolkit.git",
"directory": "packages/exec"
},
"scripts": {
"audit-moderate": "npm install && npm audit --audit-level=moderate",
"test": "echo \"Error: run tests from root\" && exit 1",
"tsc": "tsc"
},
"types": "lib/exec.d.ts",
"version": "1.0.4"
}
+21
View File
@@ -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
View File
@@ -0,0 +1,79 @@
<p align="center">
<img src="actions.png">
</p>
# Actions Http-Client
[![Http Status](https://github.com/actions/http-client/workflows/http-tests/badge.svg)](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
```
+16
View File
@@ -0,0 +1,16 @@
## Releases
## 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
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

+23
View File
@@ -0,0 +1,23 @@
import ifm = require('./interfaces');
export declare class BasicCredentialHandler implements ifm.IRequestHandler {
username: string;
password: string;
constructor(username: string, password: string);
prepareRequest(options: any): void;
canHandleAuthentication(response: ifm.IHttpClientResponse): boolean;
handleAuthentication(httpClient: ifm.IHttpClient, requestInfo: ifm.IRequestInfo, objs: any): Promise<ifm.IHttpClientResponse>;
}
export declare class BearerCredentialHandler implements ifm.IRequestHandler {
token: string;
constructor(token: string);
prepareRequest(options: any): void;
canHandleAuthentication(response: ifm.IHttpClientResponse): boolean;
handleAuthentication(httpClient: ifm.IHttpClient, requestInfo: ifm.IRequestInfo, objs: any): Promise<ifm.IHttpClientResponse>;
}
export declare class PersonalAccessTokenCredentialHandler implements ifm.IRequestHandler {
token: string;
constructor(token: string);
prepareRequest(options: any): void;
canHandleAuthentication(response: ifm.IHttpClientResponse): boolean;
handleAuthentication(httpClient: ifm.IHttpClient, requestInfo: ifm.IRequestInfo, objs: any): Promise<ifm.IHttpClientResponse>;
}
+58
View File
@@ -0,0 +1,58 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
class BasicCredentialHandler {
constructor(username, password) {
this.username = username;
this.password = password;
}
prepareRequest(options) {
options.headers['Authorization'] =
'Basic ' +
Buffer.from(this.username + ':' + this.password).toString('base64');
}
// This handler cannot handle 401
canHandleAuthentication(response) {
return false;
}
handleAuthentication(httpClient, requestInfo, objs) {
return null;
}
}
exports.BasicCredentialHandler = BasicCredentialHandler;
class BearerCredentialHandler {
constructor(token) {
this.token = token;
}
// currently implements pre-authorization
// TODO: support preAuth = false where it hooks on 401
prepareRequest(options) {
options.headers['Authorization'] = 'Bearer ' + this.token;
}
// This handler cannot handle 401
canHandleAuthentication(response) {
return false;
}
handleAuthentication(httpClient, requestInfo, objs) {
return null;
}
}
exports.BearerCredentialHandler = BearerCredentialHandler;
class PersonalAccessTokenCredentialHandler {
constructor(token) {
this.token = token;
}
// currently implements pre-authorization
// TODO: support preAuth = false where it hooks on 401
prepareRequest(options) {
options.headers['Authorization'] =
'Basic ' + Buffer.from('PAT:' + this.token).toString('base64');
}
// This handler cannot handle 401
canHandleAuthentication(response) {
return false;
}
handleAuthentication(httpClient, requestInfo, objs) {
return null;
}
}
exports.PersonalAccessTokenCredentialHandler = PersonalAccessTokenCredentialHandler;
+119
View File
@@ -0,0 +1,119 @@
/// <reference types="node" />
import http = require('http');
import ifm = require('./interfaces');
export declare enum HttpCodes {
OK = 200,
MultipleChoices = 300,
MovedPermanently = 301,
ResourceMoved = 302,
SeeOther = 303,
NotModified = 304,
UseProxy = 305,
SwitchProxy = 306,
TemporaryRedirect = 307,
PermanentRedirect = 308,
BadRequest = 400,
Unauthorized = 401,
PaymentRequired = 402,
Forbidden = 403,
NotFound = 404,
MethodNotAllowed = 405,
NotAcceptable = 406,
ProxyAuthenticationRequired = 407,
RequestTimeout = 408,
Conflict = 409,
Gone = 410,
TooManyRequests = 429,
InternalServerError = 500,
NotImplemented = 501,
BadGateway = 502,
ServiceUnavailable = 503,
GatewayTimeout = 504
}
export declare enum Headers {
Accept = "accept",
ContentType = "content-type"
}
export declare enum MediaTypes {
ApplicationJson = "application/json"
}
/**
* Returns the proxy URL, depending upon the supplied url and proxy environment variables.
* @param serverUrl The server URL where the request will be sent. For example, https://api.github.com
*/
export declare function getProxyUrl(serverUrl: string): string;
export declare class HttpClientResponse implements ifm.IHttpClientResponse {
constructor(message: http.IncomingMessage);
message: http.IncomingMessage;
readBody(): Promise<string>;
}
export declare function isHttps(requestUrl: string): boolean;
export declare class HttpClient {
userAgent: string | undefined;
handlers: ifm.IRequestHandler[];
requestOptions: ifm.IRequestOptions;
private _ignoreSslError;
private _socketTimeout;
private _allowRedirects;
private _allowRedirectDowngrade;
private _maxRedirects;
private _allowRetries;
private _maxRetries;
private _agent;
private _proxyAgent;
private _keepAlive;
private _disposed;
constructor(userAgent?: string, handlers?: ifm.IRequestHandler[], requestOptions?: ifm.IRequestOptions);
options(requestUrl: string, additionalHeaders?: ifm.IHeaders): Promise<ifm.IHttpClientResponse>;
get(requestUrl: string, additionalHeaders?: ifm.IHeaders): Promise<ifm.IHttpClientResponse>;
del(requestUrl: string, additionalHeaders?: ifm.IHeaders): Promise<ifm.IHttpClientResponse>;
post(requestUrl: string, data: string, additionalHeaders?: ifm.IHeaders): Promise<ifm.IHttpClientResponse>;
patch(requestUrl: string, data: string, additionalHeaders?: ifm.IHeaders): Promise<ifm.IHttpClientResponse>;
put(requestUrl: string, data: string, additionalHeaders?: ifm.IHeaders): Promise<ifm.IHttpClientResponse>;
head(requestUrl: string, additionalHeaders?: ifm.IHeaders): Promise<ifm.IHttpClientResponse>;
sendStream(verb: string, requestUrl: string, stream: NodeJS.ReadableStream, additionalHeaders?: ifm.IHeaders): Promise<ifm.IHttpClientResponse>;
/**
* Gets a typed object from an endpoint
* Be aware that not found returns a null. Other errors (4xx, 5xx) reject the promise
*/
getJson<T>(requestUrl: string, additionalHeaders?: ifm.IHeaders): Promise<ifm.ITypedResponse<T>>;
postJson<T>(requestUrl: string, obj: any, additionalHeaders?: ifm.IHeaders): Promise<ifm.ITypedResponse<T>>;
putJson<T>(requestUrl: string, obj: any, additionalHeaders?: ifm.IHeaders): Promise<ifm.ITypedResponse<T>>;
patchJson<T>(requestUrl: string, obj: any, additionalHeaders?: ifm.IHeaders): Promise<ifm.ITypedResponse<T>>;
/**
* Makes a raw http request.
* All other methods such as get, post, patch, and request ultimately call this.
* Prefer get, del, post and patch
*/
request(verb: string, requestUrl: string, data: string | NodeJS.ReadableStream, headers: ifm.IHeaders): Promise<ifm.IHttpClientResponse>;
/**
* Needs to be called if keepAlive is set to true in request options.
*/
dispose(): void;
/**
* Raw request.
* @param info
* @param data
*/
requestRaw(info: ifm.IRequestInfo, data: string | NodeJS.ReadableStream): Promise<ifm.IHttpClientResponse>;
/**
* Raw request with callback.
* @param info
* @param data
* @param onResult
*/
requestRawWithCallback(info: ifm.IRequestInfo, data: string | NodeJS.ReadableStream, onResult: (err: any, res: ifm.IHttpClientResponse) => void): void;
/**
* Gets an http agent. This function is useful when you need an http agent that handles
* routing through a proxy server - depending upon the url and proxy environment variables.
* @param serverUrl The server URL where the request will be sent. For example, https://api.github.com
*/
getAgent(serverUrl: string): http.Agent;
private _prepareRequest;
private _mergeHeaders;
private _getExistingOrDefaultHeader;
private _getAgent;
private _performExponentialBackoff;
private static dateTimeDeserializer;
private _processResponse;
}
+531
View File
@@ -0,0 +1,531 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const url = require("url");
const http = require("http");
const https = require("https");
const pm = require("./proxy");
let tunnel;
var HttpCodes;
(function (HttpCodes) {
HttpCodes[HttpCodes["OK"] = 200] = "OK";
HttpCodes[HttpCodes["MultipleChoices"] = 300] = "MultipleChoices";
HttpCodes[HttpCodes["MovedPermanently"] = 301] = "MovedPermanently";
HttpCodes[HttpCodes["ResourceMoved"] = 302] = "ResourceMoved";
HttpCodes[HttpCodes["SeeOther"] = 303] = "SeeOther";
HttpCodes[HttpCodes["NotModified"] = 304] = "NotModified";
HttpCodes[HttpCodes["UseProxy"] = 305] = "UseProxy";
HttpCodes[HttpCodes["SwitchProxy"] = 306] = "SwitchProxy";
HttpCodes[HttpCodes["TemporaryRedirect"] = 307] = "TemporaryRedirect";
HttpCodes[HttpCodes["PermanentRedirect"] = 308] = "PermanentRedirect";
HttpCodes[HttpCodes["BadRequest"] = 400] = "BadRequest";
HttpCodes[HttpCodes["Unauthorized"] = 401] = "Unauthorized";
HttpCodes[HttpCodes["PaymentRequired"] = 402] = "PaymentRequired";
HttpCodes[HttpCodes["Forbidden"] = 403] = "Forbidden";
HttpCodes[HttpCodes["NotFound"] = 404] = "NotFound";
HttpCodes[HttpCodes["MethodNotAllowed"] = 405] = "MethodNotAllowed";
HttpCodes[HttpCodes["NotAcceptable"] = 406] = "NotAcceptable";
HttpCodes[HttpCodes["ProxyAuthenticationRequired"] = 407] = "ProxyAuthenticationRequired";
HttpCodes[HttpCodes["RequestTimeout"] = 408] = "RequestTimeout";
HttpCodes[HttpCodes["Conflict"] = 409] = "Conflict";
HttpCodes[HttpCodes["Gone"] = 410] = "Gone";
HttpCodes[HttpCodes["TooManyRequests"] = 429] = "TooManyRequests";
HttpCodes[HttpCodes["InternalServerError"] = 500] = "InternalServerError";
HttpCodes[HttpCodes["NotImplemented"] = 501] = "NotImplemented";
HttpCodes[HttpCodes["BadGateway"] = 502] = "BadGateway";
HttpCodes[HttpCodes["ServiceUnavailable"] = 503] = "ServiceUnavailable";
HttpCodes[HttpCodes["GatewayTimeout"] = 504] = "GatewayTimeout";
})(HttpCodes = exports.HttpCodes || (exports.HttpCodes = {}));
var Headers;
(function (Headers) {
Headers["Accept"] = "accept";
Headers["ContentType"] = "content-type";
})(Headers = exports.Headers || (exports.Headers = {}));
var MediaTypes;
(function (MediaTypes) {
MediaTypes["ApplicationJson"] = "application/json";
})(MediaTypes = exports.MediaTypes || (exports.MediaTypes = {}));
/**
* Returns the proxy URL, depending upon the supplied url and proxy environment variables.
* @param serverUrl The server URL where the request will be sent. For example, https://api.github.com
*/
function getProxyUrl(serverUrl) {
let proxyUrl = pm.getProxyUrl(url.parse(serverUrl));
return proxyUrl ? proxyUrl.href : '';
}
exports.getProxyUrl = getProxyUrl;
const HttpRedirectCodes = [
HttpCodes.MovedPermanently,
HttpCodes.ResourceMoved,
HttpCodes.SeeOther,
HttpCodes.TemporaryRedirect,
HttpCodes.PermanentRedirect
];
const HttpResponseRetryCodes = [
HttpCodes.BadGateway,
HttpCodes.ServiceUnavailable,
HttpCodes.GatewayTimeout
];
const RetryableHttpVerbs = ['OPTIONS', 'GET', 'DELETE', 'HEAD'];
const ExponentialBackoffCeiling = 10;
const ExponentialBackoffTimeSlice = 5;
class HttpClientResponse {
constructor(message) {
this.message = message;
}
readBody() {
return new Promise(async (resolve, reject) => {
let output = Buffer.alloc(0);
this.message.on('data', (chunk) => {
output = Buffer.concat([output, chunk]);
});
this.message.on('end', () => {
resolve(output.toString());
});
});
}
}
exports.HttpClientResponse = HttpClientResponse;
function isHttps(requestUrl) {
let parsedUrl = url.parse(requestUrl);
return parsedUrl.protocol === 'https:';
}
exports.isHttps = isHttps;
class HttpClient {
constructor(userAgent, handlers, requestOptions) {
this._ignoreSslError = false;
this._allowRedirects = true;
this._allowRedirectDowngrade = false;
this._maxRedirects = 50;
this._allowRetries = false;
this._maxRetries = 1;
this._keepAlive = false;
this._disposed = false;
this.userAgent = userAgent;
this.handlers = handlers || [];
this.requestOptions = requestOptions;
if (requestOptions) {
if (requestOptions.ignoreSslError != null) {
this._ignoreSslError = requestOptions.ignoreSslError;
}
this._socketTimeout = requestOptions.socketTimeout;
if (requestOptions.allowRedirects != null) {
this._allowRedirects = requestOptions.allowRedirects;
}
if (requestOptions.allowRedirectDowngrade != null) {
this._allowRedirectDowngrade = requestOptions.allowRedirectDowngrade;
}
if (requestOptions.maxRedirects != null) {
this._maxRedirects = Math.max(requestOptions.maxRedirects, 0);
}
if (requestOptions.keepAlive != null) {
this._keepAlive = requestOptions.keepAlive;
}
if (requestOptions.allowRetries != null) {
this._allowRetries = requestOptions.allowRetries;
}
if (requestOptions.maxRetries != null) {
this._maxRetries = requestOptions.maxRetries;
}
}
}
options(requestUrl, additionalHeaders) {
return this.request('OPTIONS', requestUrl, null, additionalHeaders || {});
}
get(requestUrl, additionalHeaders) {
return this.request('GET', requestUrl, null, additionalHeaders || {});
}
del(requestUrl, additionalHeaders) {
return this.request('DELETE', requestUrl, null, additionalHeaders || {});
}
post(requestUrl, data, additionalHeaders) {
return this.request('POST', requestUrl, data, additionalHeaders || {});
}
patch(requestUrl, data, additionalHeaders) {
return this.request('PATCH', requestUrl, data, additionalHeaders || {});
}
put(requestUrl, data, additionalHeaders) {
return this.request('PUT', requestUrl, data, additionalHeaders || {});
}
head(requestUrl, additionalHeaders) {
return this.request('HEAD', requestUrl, null, additionalHeaders || {});
}
sendStream(verb, requestUrl, stream, additionalHeaders) {
return this.request(verb, requestUrl, stream, additionalHeaders);
}
/**
* Gets a typed object from an endpoint
* Be aware that not found returns a null. Other errors (4xx, 5xx) reject the promise
*/
async getJson(requestUrl, additionalHeaders = {}) {
additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson);
let res = await this.get(requestUrl, additionalHeaders);
return this._processResponse(res, this.requestOptions);
}
async postJson(requestUrl, obj, additionalHeaders = {}) {
let data = JSON.stringify(obj, null, 2);
additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson);
additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.ContentType, MediaTypes.ApplicationJson);
let res = await this.post(requestUrl, data, additionalHeaders);
return this._processResponse(res, this.requestOptions);
}
async putJson(requestUrl, obj, additionalHeaders = {}) {
let data = JSON.stringify(obj, null, 2);
additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson);
additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.ContentType, MediaTypes.ApplicationJson);
let res = await this.put(requestUrl, data, additionalHeaders);
return this._processResponse(res, this.requestOptions);
}
async patchJson(requestUrl, obj, additionalHeaders = {}) {
let data = JSON.stringify(obj, null, 2);
additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson);
additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.ContentType, MediaTypes.ApplicationJson);
let res = await this.patch(requestUrl, data, additionalHeaders);
return this._processResponse(res, this.requestOptions);
}
/**
* Makes a raw http request.
* All other methods such as get, post, patch, and request ultimately call this.
* Prefer get, del, post and patch
*/
async request(verb, requestUrl, data, headers) {
if (this._disposed) {
throw new Error('Client has already been disposed.');
}
let parsedUrl = url.parse(requestUrl);
let info = this._prepareRequest(verb, parsedUrl, headers);
// Only perform retries on reads since writes may not be idempotent.
let maxTries = this._allowRetries && RetryableHttpVerbs.indexOf(verb) != -1
? this._maxRetries + 1
: 1;
let numTries = 0;
let response;
while (numTries < maxTries) {
response = await this.requestRaw(info, data);
// Check if it's an authentication challenge
if (response &&
response.message &&
response.message.statusCode === HttpCodes.Unauthorized) {
let authenticationHandler;
for (let i = 0; i < this.handlers.length; i++) {
if (this.handlers[i].canHandleAuthentication(response)) {
authenticationHandler = this.handlers[i];
break;
}
}
if (authenticationHandler) {
return authenticationHandler.handleAuthentication(this, info, data);
}
else {
// We have received an unauthorized response but have no handlers to handle it.
// Let the response return to the caller.
return response;
}
}
let redirectsRemaining = this._maxRedirects;
while (HttpRedirectCodes.indexOf(response.message.statusCode) != -1 &&
this._allowRedirects &&
redirectsRemaining > 0) {
const redirectUrl = response.message.headers['location'];
if (!redirectUrl) {
// if there's no location to redirect to, we won't
break;
}
let parsedRedirectUrl = url.parse(redirectUrl);
if (parsedUrl.protocol == 'https:' &&
parsedUrl.protocol != parsedRedirectUrl.protocol &&
!this._allowRedirectDowngrade) {
throw new Error('Redirect from HTTPS to HTTP protocol. This downgrade is not allowed for security reasons. If you want to allow this behavior, set the allowRedirectDowngrade option to true.');
}
// we need to finish reading the response before reassigning response
// which will leak the open socket.
await response.readBody();
// strip authorization header if redirected to a different hostname
if (parsedRedirectUrl.hostname !== parsedUrl.hostname) {
for (let header in headers) {
// header names are case insensitive
if (header.toLowerCase() === 'authorization') {
delete headers[header];
}
}
}
// let's make the request with the new redirectUrl
info = this._prepareRequest(verb, parsedRedirectUrl, headers);
response = await this.requestRaw(info, data);
redirectsRemaining--;
}
if (HttpResponseRetryCodes.indexOf(response.message.statusCode) == -1) {
// If not a retry code, return immediately instead of retrying
return response;
}
numTries += 1;
if (numTries < maxTries) {
await response.readBody();
await this._performExponentialBackoff(numTries);
}
}
return response;
}
/**
* Needs to be called if keepAlive is set to true in request options.
*/
dispose() {
if (this._agent) {
this._agent.destroy();
}
this._disposed = true;
}
/**
* Raw request.
* @param info
* @param data
*/
requestRaw(info, data) {
return new Promise((resolve, reject) => {
let callbackForResult = function (err, res) {
if (err) {
reject(err);
}
resolve(res);
};
this.requestRawWithCallback(info, data, callbackForResult);
});
}
/**
* Raw request with callback.
* @param info
* @param data
* @param onResult
*/
requestRawWithCallback(info, data, onResult) {
let socket;
if (typeof data === 'string') {
info.options.headers['Content-Length'] = Buffer.byteLength(data, 'utf8');
}
let callbackCalled = false;
let handleResult = (err, res) => {
if (!callbackCalled) {
callbackCalled = true;
onResult(err, res);
}
};
let req = info.httpModule.request(info.options, (msg) => {
let res = new HttpClientResponse(msg);
handleResult(null, res);
});
req.on('socket', sock => {
socket = sock;
});
// If we ever get disconnected, we want the socket to timeout eventually
req.setTimeout(this._socketTimeout || 3 * 60000, () => {
if (socket) {
socket.end();
}
handleResult(new Error('Request timeout: ' + info.options.path), null);
});
req.on('error', function (err) {
// err has statusCode property
// res should have headers
handleResult(err, null);
});
if (data && typeof data === 'string') {
req.write(data, 'utf8');
}
if (data && typeof data !== 'string') {
data.on('close', function () {
req.end();
});
data.pipe(req);
}
else {
req.end();
}
}
/**
* Gets an http agent. This function is useful when you need an http agent that handles
* routing through a proxy server - depending upon the url and proxy environment variables.
* @param serverUrl The server URL where the request will be sent. For example, https://api.github.com
*/
getAgent(serverUrl) {
let parsedUrl = url.parse(serverUrl);
return this._getAgent(parsedUrl);
}
_prepareRequest(method, requestUrl, headers) {
const info = {};
info.parsedUrl = requestUrl;
const usingSsl = info.parsedUrl.protocol === 'https:';
info.httpModule = usingSsl ? https : http;
const defaultPort = usingSsl ? 443 : 80;
info.options = {};
info.options.host = info.parsedUrl.hostname;
info.options.port = info.parsedUrl.port
? parseInt(info.parsedUrl.port)
: defaultPort;
info.options.path =
(info.parsedUrl.pathname || '') + (info.parsedUrl.search || '');
info.options.method = method;
info.options.headers = this._mergeHeaders(headers);
if (this.userAgent != null) {
info.options.headers['user-agent'] = this.userAgent;
}
info.options.agent = this._getAgent(info.parsedUrl);
// gives handlers an opportunity to participate
if (this.handlers) {
this.handlers.forEach(handler => {
handler.prepareRequest(info.options);
});
}
return info;
}
_mergeHeaders(headers) {
const lowercaseKeys = obj => Object.keys(obj).reduce((c, k) => ((c[k.toLowerCase()] = obj[k]), c), {});
if (this.requestOptions && this.requestOptions.headers) {
return Object.assign({}, lowercaseKeys(this.requestOptions.headers), lowercaseKeys(headers));
}
return lowercaseKeys(headers || {});
}
_getExistingOrDefaultHeader(additionalHeaders, header, _default) {
const lowercaseKeys = obj => Object.keys(obj).reduce((c, k) => ((c[k.toLowerCase()] = obj[k]), c), {});
let clientHeader;
if (this.requestOptions && this.requestOptions.headers) {
clientHeader = lowercaseKeys(this.requestOptions.headers)[header];
}
return additionalHeaders[header] || clientHeader || _default;
}
_getAgent(parsedUrl) {
let agent;
let proxyUrl = pm.getProxyUrl(parsedUrl);
let useProxy = proxyUrl && proxyUrl.hostname;
if (this._keepAlive && useProxy) {
agent = this._proxyAgent;
}
if (this._keepAlive && !useProxy) {
agent = this._agent;
}
// if agent is already assigned use that agent.
if (!!agent) {
return agent;
}
const usingSsl = parsedUrl.protocol === 'https:';
let maxSockets = 100;
if (!!this.requestOptions) {
maxSockets = this.requestOptions.maxSockets || http.globalAgent.maxSockets;
}
if (useProxy) {
// If using proxy, need tunnel
if (!tunnel) {
tunnel = require('tunnel');
}
const agentOptions = {
maxSockets: maxSockets,
keepAlive: this._keepAlive,
proxy: {
proxyAuth: proxyUrl.auth,
host: proxyUrl.hostname,
port: proxyUrl.port
}
};
let tunnelAgent;
const overHttps = proxyUrl.protocol === 'https:';
if (usingSsl) {
tunnelAgent = overHttps ? tunnel.httpsOverHttps : tunnel.httpsOverHttp;
}
else {
tunnelAgent = overHttps ? tunnel.httpOverHttps : tunnel.httpOverHttp;
}
agent = tunnelAgent(agentOptions);
this._proxyAgent = agent;
}
// if reusing agent across request and tunneling agent isn't assigned create a new agent
if (this._keepAlive && !agent) {
const options = { keepAlive: this._keepAlive, maxSockets: maxSockets };
agent = usingSsl ? new https.Agent(options) : new http.Agent(options);
this._agent = agent;
}
// if not using private agent and tunnel agent isn't setup then use global agent
if (!agent) {
agent = usingSsl ? https.globalAgent : http.globalAgent;
}
if (usingSsl && this._ignoreSslError) {
// we don't want to set NODE_TLS_REJECT_UNAUTHORIZED=0 since that will affect request for entire process
// http.RequestOptions doesn't expose a way to modify RequestOptions.agent.options
// we have to cast it to any and change it directly
agent.options = Object.assign(agent.options || {}, {
rejectUnauthorized: false
});
}
return agent;
}
_performExponentialBackoff(retryNumber) {
retryNumber = Math.min(ExponentialBackoffCeiling, retryNumber);
const ms = ExponentialBackoffTimeSlice * Math.pow(2, retryNumber);
return new Promise(resolve => setTimeout(() => resolve(), ms));
}
static dateTimeDeserializer(key, value) {
if (typeof value === 'string') {
let a = new Date(value);
if (!isNaN(a.valueOf())) {
return a;
}
}
return value;
}
async _processResponse(res, options) {
return new Promise(async (resolve, reject) => {
const statusCode = res.message.statusCode;
const response = {
statusCode: statusCode,
result: null,
headers: {}
};
// not found leads to null obj returned
if (statusCode == HttpCodes.NotFound) {
resolve(response);
}
let obj;
let contents;
// get the result from the body
try {
contents = await res.readBody();
if (contents && contents.length > 0) {
if (options && options.deserializeDates) {
obj = JSON.parse(contents, HttpClient.dateTimeDeserializer);
}
else {
obj = JSON.parse(contents);
}
response.result = obj;
}
response.headers = res.message.headers;
}
catch (err) {
// Invalid resource (contents not json); leaving result obj null
}
// note that 3xx redirects are handled by the http layer.
if (statusCode > 299) {
let msg;
// if exception/error in body, attempt to get better error
if (obj && obj.message) {
msg = obj.message;
}
else if (contents && contents.length > 0) {
// it may be the case that the exception is in the body message as string
msg = contents;
}
else {
msg = 'Failed request: (' + statusCode + ')';
}
let err = new Error(msg);
// attach statusCode and body obj (if available) to the error object
err['statusCode'] = statusCode;
if (response.result) {
err['result'] = response.result;
}
reject(err);
}
else {
resolve(response);
}
});
}
}
exports.HttpClient = HttpClient;
+50
View File
@@ -0,0 +1,50 @@
/// <reference types="node" />
import http = require('http');
import url = require('url');
export interface IHeaders {
[key: string]: any;
}
export interface IHttpClient {
options(requestUrl: string, additionalHeaders?: IHeaders): Promise<IHttpClientResponse>;
get(requestUrl: string, additionalHeaders?: IHeaders): Promise<IHttpClientResponse>;
del(requestUrl: string, additionalHeaders?: IHeaders): Promise<IHttpClientResponse>;
post(requestUrl: string, data: string, additionalHeaders?: IHeaders): Promise<IHttpClientResponse>;
patch(requestUrl: string, data: string, additionalHeaders?: IHeaders): Promise<IHttpClientResponse>;
put(requestUrl: string, data: string, additionalHeaders?: IHeaders): Promise<IHttpClientResponse>;
sendStream(verb: string, requestUrl: string, stream: NodeJS.ReadableStream, additionalHeaders?: IHeaders): Promise<IHttpClientResponse>;
request(verb: string, requestUrl: string, data: string | NodeJS.ReadableStream, headers: IHeaders): Promise<IHttpClientResponse>;
requestRaw(info: IRequestInfo, data: string | NodeJS.ReadableStream): Promise<IHttpClientResponse>;
requestRawWithCallback(info: IRequestInfo, data: string | NodeJS.ReadableStream, onResult: (err: any, res: IHttpClientResponse) => void): void;
}
export interface IRequestHandler {
prepareRequest(options: http.RequestOptions): void;
canHandleAuthentication(response: IHttpClientResponse): boolean;
handleAuthentication(httpClient: IHttpClient, requestInfo: IRequestInfo, objs: any): Promise<IHttpClientResponse>;
}
export interface IHttpClientResponse {
message: http.IncomingMessage;
readBody(): Promise<string>;
}
export interface IRequestInfo {
options: http.RequestOptions;
parsedUrl: url.Url;
httpModule: any;
}
export interface IRequestOptions {
headers?: IHeaders;
socketTimeout?: number;
ignoreSslError?: boolean;
allowRedirects?: boolean;
allowRedirectDowngrade?: boolean;
maxRedirects?: number;
maxSockets?: number;
keepAlive?: boolean;
deserializeDates?: boolean;
allowRetries?: boolean;
maxRetries?: number;
}
export interface ITypedResponse<T> {
statusCode: number;
result: T | null;
headers: Object;
}
+2
View File
@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
+67
View File
@@ -0,0 +1,67 @@
{
"_from": "@actions/http-client@^1.0.8",
"_id": "@actions/http-client@1.0.8",
"_inBundle": false,
"_integrity": "sha512-G4JjJ6f9Hb3Zvejj+ewLLKLf99ZC+9v+yCxoYf9vSyH+WkzPLB2LuUtRMGNkooMqdugGBFStIKXOuvH1W+EctA==",
"_location": "/@actions/http-client",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "@actions/http-client@^1.0.8",
"name": "@actions/http-client",
"escapedName": "@actions%2fhttp-client",
"scope": "@actions",
"rawSpec": "^1.0.8",
"saveSpec": null,
"fetchSpec": "^1.0.8"
},
"_requiredBy": [
"/@actions/tool-cache"
],
"_resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.8.tgz",
"_shasum": "8bd76e8eca89dc8bcf619aa128eba85f7a39af45",
"_spec": "@actions/http-client@^1.0.8",
"_where": "/Users/aj/repos/k8s-deploy/node_modules/@actions/tool-cache",
"author": {
"name": "GitHub, Inc."
},
"bugs": {
"url": "https://github.com/actions/http-client/issues"
},
"bundleDependencies": false,
"dependencies": {
"tunnel": "0.0.6"
},
"deprecated": false,
"description": "Actions Http Client",
"devDependencies": {
"@types/jest": "^25.1.4",
"@types/node": "^12.12.31",
"jest": "^25.1.0",
"prettier": "^2.0.4",
"proxy": "^1.0.1",
"ts-jest": "^25.2.1",
"typescript": "^3.8.3"
},
"homepage": "https://github.com/actions/http-client#readme",
"keywords": [
"Actions",
"Http"
],
"license": "MIT",
"main": "index.js",
"name": "@actions/http-client",
"repository": {
"type": "git",
"url": "git+https://github.com/actions/http-client.git"
},
"scripts": {
"audit-check": "npm audit --audit-level=moderate",
"build": "rm -Rf ./_out && tsc && cp package*.json ./_out && cp *.md ./_out && cp LICENSE ./_out && cp actions.png ./_out",
"format": "prettier --write *.ts && prettier --write **/*.ts",
"format-check": "prettier --check *.ts && prettier --check **/*.ts",
"test": "jest"
},
"version": "1.0.8"
}
+4
View File
@@ -0,0 +1,4 @@
/// <reference types="node" />
import * as url from 'url';
export declare function getProxyUrl(reqUrl: url.Url): url.Url | undefined;
export declare function checkBypass(reqUrl: url.Url): boolean;
+58
View File
@@ -0,0 +1,58 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const url = require("url");
function getProxyUrl(reqUrl) {
let usingSsl = reqUrl.protocol === 'https:';
let proxyUrl;
if (checkBypass(reqUrl)) {
return proxyUrl;
}
let proxyVar;
if (usingSsl) {
proxyVar = process.env['https_proxy'] || process.env['HTTPS_PROXY'];
}
else {
proxyVar = process.env['http_proxy'] || process.env['HTTP_PROXY'];
}
if (proxyVar) {
proxyUrl = url.parse(proxyVar);
}
return proxyUrl;
}
exports.getProxyUrl = getProxyUrl;
function checkBypass(reqUrl) {
if (!reqUrl.hostname) {
return false;
}
let noProxy = process.env['no_proxy'] || process.env['NO_PROXY'] || '';
if (!noProxy) {
return false;
}
// Determine the request port
let reqPort;
if (reqUrl.port) {
reqPort = Number(reqUrl.port);
}
else if (reqUrl.protocol === 'http:') {
reqPort = 80;
}
else if (reqUrl.protocol === 'https:') {
reqPort = 443;
}
// Format the request hostname and hostname with port
let upperReqHosts = [reqUrl.hostname.toUpperCase()];
if (typeof reqPort === 'number') {
upperReqHosts.push(`${upperReqHosts[0]}:${reqPort}`);
}
// Compare request host against noproxy
for (let upperNoProxyItem of noProxy
.split(',')
.map(x => x.trim().toUpperCase())
.filter(x => x)) {
if (upperReqHosts.some(x => x === upperNoProxyItem)) {
return true;
}
}
return false;
}
exports.checkBypass = checkBypass;
+53
View File
@@ -0,0 +1,53 @@
# `@actions/io`
> Core functions for cli filesystem scenarios
## Usage
#### mkdir -p
Recursively make a directory. Follows rules specified in [man mkdir](https://linux.die.net/man/1/mkdir) with the `-p` option specified:
```js
const io = require('@actions/io');
await io.mkdirP('path/to/make');
```
#### cp/mv
Copy or move files or folders. Follows rules specified in [man cp](https://linux.die.net/man/1/cp) and [man mv](https://linux.die.net/man/1/mv):
```js
const io = require('@actions/io');
// Recursive must be true for directories
const options = { recursive: true, force: false }
await io.cp('path/to/directory', 'path/to/dest', options);
await io.mv('path/to/file', 'path/to/dest');
```
#### rm -rf
Remove a file or folder recursively. Follows rules specified in [man rm](https://linux.die.net/man/1/rm) with the `-r` and `-f` rules specified.
```js
const io = require('@actions/io');
await io.rmRF('path/to/directory');
await io.rmRF('path/to/file');
```
#### which
Get the path to a tool and resolves via paths. Follows the rules specified in [man which](https://linux.die.net/man/1/which).
```js
const exec = require('@actions/exec');
const io = require('@actions/io');
const pythonPath: string = await io.which('python', true)
await exec.exec(`"${pythonPath}"`, ['main.py']);
```
+29
View File
@@ -0,0 +1,29 @@
/// <reference types="node" />
import * as fs from 'fs';
export declare const chmod: typeof fs.promises.chmod, copyFile: typeof fs.promises.copyFile, lstat: typeof fs.promises.lstat, mkdir: typeof fs.promises.mkdir, readdir: typeof fs.promises.readdir, readlink: typeof fs.promises.readlink, rename: typeof fs.promises.rename, rmdir: typeof fs.promises.rmdir, stat: typeof fs.promises.stat, symlink: typeof fs.promises.symlink, unlink: typeof fs.promises.unlink;
export declare const IS_WINDOWS: boolean;
export declare function exists(fsPath: string): Promise<boolean>;
export declare function isDirectory(fsPath: string, useStat?: boolean): Promise<boolean>;
/**
* On OSX/Linux, true if path starts with '/'. On Windows, true for paths like:
* \, \hello, \\hello\share, C:, and C:\hello (and corresponding alternate separator cases).
*/
export declare function isRooted(p: string): boolean;
/**
* Recursively create a directory at `fsPath`.
*
* This implementation is optimistic, meaning it attempts to create the full
* path first, and backs up the path stack from there.
*
* @param fsPath The path to create
* @param maxDepth The maximum recursion depth
* @param depth The current recursion depth
*/
export declare function mkdirP(fsPath: string, maxDepth?: number, depth?: number): Promise<void>;
/**
* Best effort attempt to determine whether a file exists and is executable.
* @param filePath file path to check
* @param extensions additional file extensions to try
* @return if file exists and is executable, returns the file path. otherwise empty string.
*/
export declare function tryGetExecutablePath(filePath: string, extensions: string[]): Promise<string>;
+195
View File
@@ -0,0 +1,195 @@
"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 _a;
Object.defineProperty(exports, "__esModule", { value: true });
const assert_1 = require("assert");
const fs = require("fs");
const path = require("path");
_a = fs.promises, exports.chmod = _a.chmod, exports.copyFile = _a.copyFile, exports.lstat = _a.lstat, exports.mkdir = _a.mkdir, exports.readdir = _a.readdir, exports.readlink = _a.readlink, exports.rename = _a.rename, exports.rmdir = _a.rmdir, exports.stat = _a.stat, exports.symlink = _a.symlink, exports.unlink = _a.unlink;
exports.IS_WINDOWS = process.platform === 'win32';
function exists(fsPath) {
return __awaiter(this, void 0, void 0, function* () {
try {
yield exports.stat(fsPath);
}
catch (err) {
if (err.code === 'ENOENT') {
return false;
}
throw err;
}
return true;
});
}
exports.exists = exists;
function isDirectory(fsPath, useStat = false) {
return __awaiter(this, void 0, void 0, function* () {
const stats = useStat ? yield exports.stat(fsPath) : yield exports.lstat(fsPath);
return stats.isDirectory();
});
}
exports.isDirectory = isDirectory;
/**
* On OSX/Linux, true if path starts with '/'. On Windows, true for paths like:
* \, \hello, \\hello\share, C:, and C:\hello (and corresponding alternate separator cases).
*/
function isRooted(p) {
p = normalizeSeparators(p);
if (!p) {
throw new Error('isRooted() parameter "p" cannot be empty');
}
if (exports.IS_WINDOWS) {
return (p.startsWith('\\') || /^[A-Z]:/i.test(p) // e.g. \ or \hello or \\hello
); // e.g. C: or C:\hello
}
return p.startsWith('/');
}
exports.isRooted = isRooted;
/**
* Recursively create a directory at `fsPath`.
*
* This implementation is optimistic, meaning it attempts to create the full
* path first, and backs up the path stack from there.
*
* @param fsPath The path to create
* @param maxDepth The maximum recursion depth
* @param depth The current recursion depth
*/
function mkdirP(fsPath, maxDepth = 1000, depth = 1) {
return __awaiter(this, void 0, void 0, function* () {
assert_1.ok(fsPath, 'a path argument must be provided');
fsPath = path.resolve(fsPath);
if (depth >= maxDepth)
return exports.mkdir(fsPath);
try {
yield exports.mkdir(fsPath);
return;
}
catch (err) {
switch (err.code) {
case 'ENOENT': {
yield mkdirP(path.dirname(fsPath), maxDepth, depth + 1);
yield exports.mkdir(fsPath);
return;
}
default: {
let stats;
try {
stats = yield exports.stat(fsPath);
}
catch (err2) {
throw err;
}
if (!stats.isDirectory())
throw err;
}
}
}
});
}
exports.mkdirP = mkdirP;
/**
* Best effort attempt to determine whether a file exists and is executable.
* @param filePath file path to check
* @param extensions additional file extensions to try
* @return if file exists and is executable, returns the file path. otherwise empty string.
*/
function tryGetExecutablePath(filePath, extensions) {
return __awaiter(this, void 0, void 0, function* () {
let stats = undefined;
try {
// test file exists
stats = yield exports.stat(filePath);
}
catch (err) {
if (err.code !== 'ENOENT') {
// eslint-disable-next-line no-console
console.log(`Unexpected error attempting to determine if executable file exists '${filePath}': ${err}`);
}
}
if (stats && stats.isFile()) {
if (exports.IS_WINDOWS) {
// on Windows, test for valid extension
const upperExt = path.extname(filePath).toUpperCase();
if (extensions.some(validExt => validExt.toUpperCase() === upperExt)) {
return filePath;
}
}
else {
if (isUnixExecutable(stats)) {
return filePath;
}
}
}
// try each extension
const originalFilePath = filePath;
for (const extension of extensions) {
filePath = originalFilePath + extension;
stats = undefined;
try {
stats = yield exports.stat(filePath);
}
catch (err) {
if (err.code !== 'ENOENT') {
// eslint-disable-next-line no-console
console.log(`Unexpected error attempting to determine if executable file exists '${filePath}': ${err}`);
}
}
if (stats && stats.isFile()) {
if (exports.IS_WINDOWS) {
// preserve the case of the actual file (since an extension was appended)
try {
const directory = path.dirname(filePath);
const upperName = path.basename(filePath).toUpperCase();
for (const actualName of yield exports.readdir(directory)) {
if (upperName === actualName.toUpperCase()) {
filePath = path.join(directory, actualName);
break;
}
}
}
catch (err) {
// eslint-disable-next-line no-console
console.log(`Unexpected error attempting to determine the actual case of the file '${filePath}': ${err}`);
}
return filePath;
}
else {
if (isUnixExecutable(stats)) {
return filePath;
}
}
}
}
return '';
});
}
exports.tryGetExecutablePath = tryGetExecutablePath;
function normalizeSeparators(p) {
p = p || '';
if (exports.IS_WINDOWS) {
// convert slashes on Windows
p = p.replace(/\//g, '\\');
// remove redundant slashes
return p.replace(/\\\\+/g, '\\');
}
// remove redundant slashes
return p.replace(/\/\/+/g, '/');
}
// on Mac/Linux, test the execute bit
// R W X R W X R W X
// 256 128 64 32 16 8 4 2 1
function isUnixExecutable(stats) {
return ((stats.mode & 1) > 0 ||
((stats.mode & 8) > 0 && stats.gid === process.getgid()) ||
((stats.mode & 64) > 0 && stats.uid === process.getuid()));
}
//# sourceMappingURL=io-util.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"io-util.js","sourceRoot":"","sources":["../src/io-util.ts"],"names":[],"mappings":"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"}
+56
View File
@@ -0,0 +1,56 @@
/**
* Interface for cp/mv options
*/
export interface CopyOptions {
/** Optional. Whether to recursively copy all subdirectories. Defaults to false */
recursive?: boolean;
/** Optional. Whether to overwrite existing files in the destination. Defaults to true */
force?: boolean;
}
/**
* Interface for cp/mv options
*/
export interface MoveOptions {
/** Optional. Whether to overwrite existing files in the destination. Defaults to true */
force?: boolean;
}
/**
* Copies a file or folder.
* Based off of shelljs - https://github.com/shelljs/shelljs/blob/9237f66c52e5daa40458f94f9565e18e8132f5a6/src/cp.js
*
* @param source source path
* @param dest destination path
* @param options optional. See CopyOptions.
*/
export declare function cp(source: string, dest: string, options?: CopyOptions): Promise<void>;
/**
* Moves a path.
*
* @param source source path
* @param dest destination path
* @param options optional. See MoveOptions.
*/
export declare function mv(source: string, dest: string, options?: MoveOptions): Promise<void>;
/**
* Remove a path recursively with force
*
* @param inputPath path to remove
*/
export declare function rmRF(inputPath: string): Promise<void>;
/**
* Make a directory. Creates the full path with folders in between
* Will throw if it fails
*
* @param fsPath path to create
* @returns Promise<void>
*/
export declare function mkdirP(fsPath: string): Promise<void>;
/**
* Returns path of a tool had the tool actually been invoked. Resolves via paths.
* If you check and the tool does not exist, it will throw.
*
* @param tool name of the tool
* @param check whether to check if tool exists
* @returns Promise<string> path to tool
*/
export declare function which(tool: string, check?: boolean): Promise<string>;
+290
View File
@@ -0,0 +1,290 @@
"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 childProcess = require("child_process");
const path = require("path");
const util_1 = require("util");
const ioUtil = require("./io-util");
const exec = util_1.promisify(childProcess.exec);
/**
* Copies a file or folder.
* Based off of shelljs - https://github.com/shelljs/shelljs/blob/9237f66c52e5daa40458f94f9565e18e8132f5a6/src/cp.js
*
* @param source source path
* @param dest destination path
* @param options optional. See CopyOptions.
*/
function cp(source, dest, options = {}) {
return __awaiter(this, void 0, void 0, function* () {
const { force, recursive } = readCopyOptions(options);
const destStat = (yield ioUtil.exists(dest)) ? yield ioUtil.stat(dest) : null;
// Dest is an existing file, but not forcing
if (destStat && destStat.isFile() && !force) {
return;
}
// If dest is an existing directory, should copy inside.
const newDest = destStat && destStat.isDirectory()
? path.join(dest, path.basename(source))
: dest;
if (!(yield ioUtil.exists(source))) {
throw new Error(`no such file or directory: ${source}`);
}
const sourceStat = yield ioUtil.stat(source);
if (sourceStat.isDirectory()) {
if (!recursive) {
throw new Error(`Failed to copy. ${source} is a directory, but tried to copy without recursive flag.`);
}
else {
yield cpDirRecursive(source, newDest, 0, force);
}
}
else {
if (path.relative(source, newDest) === '') {
// a file cannot be copied to itself
throw new Error(`'${newDest}' and '${source}' are the same file`);
}
yield copyFile(source, newDest, force);
}
});
}
exports.cp = cp;
/**
* Moves a path.
*
* @param source source path
* @param dest destination path
* @param options optional. See MoveOptions.
*/
function mv(source, dest, options = {}) {
return __awaiter(this, void 0, void 0, function* () {
if (yield ioUtil.exists(dest)) {
let destExists = true;
if (yield ioUtil.isDirectory(dest)) {
// If dest is directory copy src into dest
dest = path.join(dest, path.basename(source));
destExists = yield ioUtil.exists(dest);
}
if (destExists) {
if (options.force == null || options.force) {
yield rmRF(dest);
}
else {
throw new Error('Destination already exists');
}
}
}
yield mkdirP(path.dirname(dest));
yield ioUtil.rename(source, dest);
});
}
exports.mv = mv;
/**
* Remove a path recursively with force
*
* @param inputPath path to remove
*/
function rmRF(inputPath) {
return __awaiter(this, void 0, void 0, function* () {
if (ioUtil.IS_WINDOWS) {
// Node doesn't provide a delete operation, only an unlink function. This means that if the file is being used by another
// program (e.g. antivirus), it won't be deleted. To address this, we shell out the work to rd/del.
try {
if (yield ioUtil.isDirectory(inputPath, true)) {
yield exec(`rd /s /q "${inputPath}"`);
}
else {
yield exec(`del /f /a "${inputPath}"`);
}
}
catch (err) {
// if you try to delete a file that doesn't exist, desired result is achieved
// other errors are valid
if (err.code !== 'ENOENT')
throw err;
}
// Shelling out fails to remove a symlink folder with missing source, this unlink catches that
try {
yield ioUtil.unlink(inputPath);
}
catch (err) {
// if you try to delete a file that doesn't exist, desired result is achieved
// other errors are valid
if (err.code !== 'ENOENT')
throw err;
}
}
else {
let isDir = false;
try {
isDir = yield ioUtil.isDirectory(inputPath);
}
catch (err) {
// if you try to delete a file that doesn't exist, desired result is achieved
// other errors are valid
if (err.code !== 'ENOENT')
throw err;
return;
}
if (isDir) {
yield exec(`rm -rf "${inputPath}"`);
}
else {
yield ioUtil.unlink(inputPath);
}
}
});
}
exports.rmRF = rmRF;
/**
* Make a directory. Creates the full path with folders in between
* Will throw if it fails
*
* @param fsPath path to create
* @returns Promise<void>
*/
function mkdirP(fsPath) {
return __awaiter(this, void 0, void 0, function* () {
yield ioUtil.mkdirP(fsPath);
});
}
exports.mkdirP = mkdirP;
/**
* Returns path of a tool had the tool actually been invoked. Resolves via paths.
* If you check and the tool does not exist, it will throw.
*
* @param tool name of the tool
* @param check whether to check if tool exists
* @returns Promise<string> path to tool
*/
function which(tool, check) {
return __awaiter(this, void 0, void 0, function* () {
if (!tool) {
throw new Error("parameter 'tool' is required");
}
// recursive when check=true
if (check) {
const result = yield which(tool, false);
if (!result) {
if (ioUtil.IS_WINDOWS) {
throw new Error(`Unable to locate executable file: ${tool}. Please verify either the file path exists or the file can be found within a directory specified by the PATH environment variable. Also verify the file has a valid extension for an executable file.`);
}
else {
throw new Error(`Unable to locate executable file: ${tool}. Please verify either the file path exists or the file can be found within a directory specified by the PATH environment variable. Also check the file mode to verify the file is executable.`);
}
}
}
try {
// build the list of extensions to try
const extensions = [];
if (ioUtil.IS_WINDOWS && process.env.PATHEXT) {
for (const extension of process.env.PATHEXT.split(path.delimiter)) {
if (extension) {
extensions.push(extension);
}
}
}
// if it's rooted, return it if exists. otherwise return empty.
if (ioUtil.isRooted(tool)) {
const filePath = yield ioUtil.tryGetExecutablePath(tool, extensions);
if (filePath) {
return filePath;
}
return '';
}
// if any path separators, return empty
if (tool.includes('/') || (ioUtil.IS_WINDOWS && tool.includes('\\'))) {
return '';
}
// build the list of directories
//
// Note, technically "where" checks the current directory on Windows. From a toolkit perspective,
// it feels like we should not do this. Checking the current directory seems like more of a use
// case of a shell, and the which() function exposed by the toolkit should strive for consistency
// across platforms.
const directories = [];
if (process.env.PATH) {
for (const p of process.env.PATH.split(path.delimiter)) {
if (p) {
directories.push(p);
}
}
}
// return the first match
for (const directory of directories) {
const filePath = yield ioUtil.tryGetExecutablePath(directory + path.sep + tool, extensions);
if (filePath) {
return filePath;
}
}
return '';
}
catch (err) {
throw new Error(`which failed with message ${err.message}`);
}
});
}
exports.which = which;
function readCopyOptions(options) {
const force = options.force == null ? true : options.force;
const recursive = Boolean(options.recursive);
return { force, recursive };
}
function cpDirRecursive(sourceDir, destDir, currentDepth, force) {
return __awaiter(this, void 0, void 0, function* () {
// Ensure there is not a run away recursive copy
if (currentDepth >= 255)
return;
currentDepth++;
yield mkdirP(destDir);
const files = yield ioUtil.readdir(sourceDir);
for (const fileName of files) {
const srcFile = `${sourceDir}/${fileName}`;
const destFile = `${destDir}/${fileName}`;
const srcFileStat = yield ioUtil.lstat(srcFile);
if (srcFileStat.isDirectory()) {
// Recurse
yield cpDirRecursive(srcFile, destFile, currentDepth, force);
}
else {
yield copyFile(srcFile, destFile, force);
}
}
// Change the mode for the newly created directory
yield ioUtil.chmod(destDir, (yield ioUtil.stat(sourceDir)).mode);
});
}
// Buffered file copy
function copyFile(srcFile, destFile, force) {
return __awaiter(this, void 0, void 0, function* () {
if ((yield ioUtil.lstat(srcFile)).isSymbolicLink()) {
// unlink/re-link it
try {
yield ioUtil.lstat(destFile);
yield ioUtil.unlink(destFile);
}
catch (e) {
// Try to override file permission
if (e.code === 'EPERM') {
yield ioUtil.chmod(destFile, '0666');
yield ioUtil.unlink(destFile);
}
// other errors = it doesn't exist, no work to do
}
// Copy over symlink
const symlinkFull = yield ioUtil.readlink(srcFile);
yield ioUtil.symlink(symlinkFull, destFile, ioUtil.IS_WINDOWS ? 'junction' : null);
}
else if (!(yield ioUtil.exists(destFile)) || force) {
yield ioUtil.copyFile(srcFile, destFile);
}
});
}
//# sourceMappingURL=io.js.map
+1
View File
File diff suppressed because one or more lines are too long
+65
View File
@@ -0,0 +1,65 @@
{
"_from": "@actions/io@1.0.2",
"_id": "@actions/io@1.0.2",
"_inBundle": false,
"_integrity": "sha512-J8KuFqVPr3p6U8W93DOXlXW6zFvrQAJANdS+vw0YhusLIq+bszW8zmK2Fh1C2kDPX8FMvwIl1OUcFgvJoXLbAg==",
"_location": "/@actions/io",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "@actions/io@1.0.2",
"name": "@actions/io",
"escapedName": "@actions%2fio",
"scope": "@actions",
"rawSpec": "1.0.2",
"saveSpec": null,
"fetchSpec": "1.0.2"
},
"_requiredBy": [
"/",
"/@actions/exec",
"/@actions/tool-cache"
],
"_resolved": "https://registry.npmjs.org/@actions/io/-/io-1.0.2.tgz",
"_shasum": "2f614b6e69ce14d191180451eb38e6576a6e6b27",
"_spec": "@actions/io@1.0.2",
"_where": "/Users/aj/repos/k8s-deploy",
"bugs": {
"url": "https://github.com/actions/toolkit/issues"
},
"bundleDependencies": false,
"deprecated": false,
"description": "Actions io lib",
"directories": {
"lib": "lib",
"test": "__tests__"
},
"files": [
"lib"
],
"homepage": "https://github.com/actions/toolkit/tree/master/packages/io",
"keywords": [
"github",
"actions",
"io"
],
"license": "MIT",
"main": "lib/io.js",
"name": "@actions/io",
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "git+https://github.com/actions/toolkit.git",
"directory": "packages/io"
},
"scripts": {
"audit-moderate": "npm install && npm audit --audit-level=moderate",
"test": "echo \"Error: run tests from root\" && exit 1",
"tsc": "tsc"
},
"types": "lib/io.d.ts",
"version": "1.0.2"
}
+86
View File
@@ -0,0 +1,86 @@
# `@actions/tool-cache`
> Functions necessary for downloading and caching tools.
## Usage
#### Download
You can use this to download tools (or other files) from a download URL:
```js
const tc = require('@actions/tool-cache');
const node12Path = await tc.downloadTool('https://nodejs.org/dist/v12.7.0/node-v12.7.0-linux-x64.tar.gz');
```
#### Extract
These can then be extracted in platform specific ways:
```js
const tc = require('@actions/tool-cache');
if (process.platform === 'win32') {
const node12Path = await tc.downloadTool('https://nodejs.org/dist/v12.7.0/node-v12.7.0-win-x64.zip');
const node12ExtractedFolder = await tc.extractZip(node12Path, 'path/to/extract/to');
// Or alternately
const node12Path = await tc.downloadTool('https://nodejs.org/dist/v12.7.0/node-v12.7.0-win-x64.7z');
const node12ExtractedFolder = await tc.extract7z(node12Path, 'path/to/extract/to');
}
else if (process.platform === 'darwin') {
const node12Path = await tc.downloadTool('https://nodejs.org/dist/v12.7.0/node-v12.7.0.pkg');
const node12ExtractedFolder = await tc.extractXar(node12Path, 'path/to/extract/to');
}
else {
const node12Path = await tc.downloadTool('https://nodejs.org/dist/v12.7.0/node-v12.7.0-linux-x64.tar.gz');
const node12ExtractedFolder = await tc.extractTar(node12Path, 'path/to/extract/to');
}
```
#### Cache
Finally, you can cache these directories in our tool-cache. This is useful if you want to switch back and forth between versions of a tool, or save a tool between runs for self-hosted runners.
You'll often want to add it to the path as part of this step:
```js
const tc = require('@actions/tool-cache');
const core = require('@actions/core');
const node12Path = await tc.downloadTool('https://nodejs.org/dist/v12.7.0/node-v12.7.0-linux-x64.tar.gz');
const node12ExtractedFolder = await tc.extractTar(node12Path, 'path/to/extract/to');
const cachedPath = await tc.cacheDir(node12ExtractedFolder, 'node', '12.7.0');
core.addPath(cachedPath);
```
You can also cache files for reuse.
```js
const tc = require('@actions/tool-cache');
const cachedPath = await tc.cacheFile('path/to/exe', 'destFileName.exe', 'myExeName', '1.1.0');
```
#### Find
Finally, you can find directories and files you've previously cached:
```js
const tc = require('@actions/tool-cache');
const core = require('@actions/core');
const nodeDirectory = tc.find('node', '12.x', 'x64');
core.addPath(nodeDirectory);
```
You can even find all cached versions of a tool:
```js
const tc = require('@actions/tool-cache');
const allNodeVersions = tc.findAllVersions('node');
console.log(`Versions of node available: ${allNodeVersions}`);
```
+16
View File
@@ -0,0 +1,16 @@
export interface IToolReleaseFile {
filename: string;
platform: string;
platform_version?: string;
arch: string;
download_url: string;
}
export interface IToolRelease {
version: string;
stable: boolean;
release_url: string;
files: IToolReleaseFile[];
}
export declare function _findMatch(versionSpec: string, stable: boolean, candidates: IToolRelease[], archFilter: string): Promise<IToolRelease | undefined>;
export declare function _getOsVersion(): string;
export declare function _readLinuxVersionFile(): string;
+106
View File
@@ -0,0 +1,106 @@
"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 semver = __importStar(require("semver"));
const core_1 = require("@actions/core");
// needs to be require for core node modules to be mocked
/* eslint @typescript-eslint/no-require-imports: 0 */
const os = require("os");
const cp = require("child_process");
const fs = require("fs");
function _findMatch(versionSpec, stable, candidates, archFilter) {
return __awaiter(this, void 0, void 0, function* () {
const platFilter = os.platform();
let result;
let match;
let file;
for (const candidate of candidates) {
const version = candidate.version;
core_1.debug(`check ${version} satisfies ${versionSpec}`);
if (semver.satisfies(version, versionSpec) &&
(!stable || candidate.stable === stable)) {
file = candidate.files.find(item => {
core_1.debug(`${item.arch}===${archFilter} && ${item.platform}===${platFilter}`);
let chk = item.arch === archFilter && item.platform === platFilter;
if (chk && item.platform_version) {
const osVersion = module.exports._getOsVersion();
if (osVersion === item.platform_version) {
chk = true;
}
else {
chk = semver.satisfies(osVersion, item.platform_version);
}
}
return chk;
});
if (file) {
core_1.debug(`matched ${candidate.version}`);
match = candidate;
break;
}
}
}
if (match && file) {
// clone since we're mutating the file list to be only the file that matches
result = Object.assign({}, match);
result.files = [file];
}
return result;
});
}
exports._findMatch = _findMatch;
function _getOsVersion() {
// TODO: add windows and other linux, arm variants
// right now filtering on version is only an ubuntu and macos scenario for tools we build for hosted (python)
const plat = os.platform();
let version = '';
if (plat === 'darwin') {
version = cp.execSync('sw_vers -productVersion').toString();
}
else if (plat === 'linux') {
// lsb_release process not in some containers, readfile
// Run cat /etc/lsb-release
// DISTRIB_ID=Ubuntu
// DISTRIB_RELEASE=18.04
// DISTRIB_CODENAME=bionic
// DISTRIB_DESCRIPTION="Ubuntu 18.04.4 LTS"
const lsbContents = module.exports._readLinuxVersionFile();
if (lsbContents) {
const lines = lsbContents.split('\n');
for (const line of lines) {
const parts = line.split('=');
if (parts.length === 2 && parts[0].trim() === 'DISTRIB_RELEASE') {
version = parts[1].trim();
break;
}
}
}
}
return version;
}
exports._getOsVersion = _getOsVersion;
function _readLinuxVersionFile() {
const lsbFile = '/etc/lsb-release';
let contents = '';
if (fs.existsSync(lsbFile)) {
contents = fs.readFileSync(lsbFile).toString();
}
return contents;
}
exports._readLinuxVersionFile = _readLinuxVersionFile;
//# sourceMappingURL=manifest.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"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"}
+12
View File
@@ -0,0 +1,12 @@
/**
* Internal class for retries
*/
export declare class RetryHelper {
private maxAttempts;
private minSeconds;
private maxSeconds;
constructor(maxAttempts: number, minSeconds: number, maxSeconds: number);
execute<T>(action: () => Promise<T>, isRetryable?: (e: Error) => boolean): Promise<T>;
private getSleepAmount;
private sleep;
}
+70
View File
@@ -0,0 +1,70 @@
"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"));
/**
* Internal class for retries
*/
class RetryHelper {
constructor(maxAttempts, minSeconds, maxSeconds) {
if (maxAttempts < 1) {
throw new Error('max attempts should be greater than or equal to 1');
}
this.maxAttempts = maxAttempts;
this.minSeconds = Math.floor(minSeconds);
this.maxSeconds = Math.floor(maxSeconds);
if (this.minSeconds > this.maxSeconds) {
throw new Error('min seconds should be less than or equal to max seconds');
}
}
execute(action, isRetryable) {
return __awaiter(this, void 0, void 0, function* () {
let attempt = 1;
while (attempt < this.maxAttempts) {
// Try
try {
return yield action();
}
catch (err) {
if (isRetryable && !isRetryable(err)) {
throw err;
}
core.info(err.message);
}
// Sleep
const seconds = this.getSleepAmount();
core.info(`Waiting ${seconds} seconds before trying again`);
yield this.sleep(seconds);
attempt++;
}
// Last attempt
return yield action();
});
}
getSleepAmount() {
return (Math.floor(Math.random() * (this.maxSeconds - this.minSeconds + 1)) +
this.minSeconds);
}
sleep(seconds) {
return __awaiter(this, void 0, void 0, function* () {
return new Promise(resolve => setTimeout(resolve, seconds * 1000));
});
}
}
exports.RetryHelper = RetryHelper;
//# sourceMappingURL=retry-helper.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"retry-helper.js","sourceRoot":"","sources":["../src/retry-helper.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAAA,oDAAqC;AAErC;;GAEG;AACH,MAAa,WAAW;IAKtB,YAAY,WAAmB,EAAE,UAAkB,EAAE,UAAkB;QACrE,IAAI,WAAW,GAAG,CAAC,EAAE;YACnB,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAA;SACrE;QAED,IAAI,CAAC,WAAW,GAAG,WAAW,CAAA;QAC9B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;QACxC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;QACxC,IAAI,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,EAAE;YACrC,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAA;SAC3E;IACH,CAAC;IAEK,OAAO,CACX,MAAwB,EACxB,WAAmC;;YAEnC,IAAI,OAAO,GAAG,CAAC,CAAA;YACf,OAAO,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE;gBACjC,MAAM;gBACN,IAAI;oBACF,OAAO,MAAM,MAAM,EAAE,CAAA;iBACtB;gBAAC,OAAO,GAAG,EAAE;oBACZ,IAAI,WAAW,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE;wBACpC,MAAM,GAAG,CAAA;qBACV;oBAED,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;iBACvB;gBAED,QAAQ;gBACR,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,EAAE,CAAA;gBACrC,IAAI,CAAC,IAAI,CAAC,WAAW,OAAO,8BAA8B,CAAC,CAAA;gBAC3D,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;gBACzB,OAAO,EAAE,CAAA;aACV;YAED,eAAe;YACf,OAAO,MAAM,MAAM,EAAE,CAAA;QACvB,CAAC;KAAA;IAEO,cAAc;QACpB,OAAO,CACL,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;YACnE,IAAI,CAAC,UAAU,CAChB,CAAA;IACH,CAAC;IAEa,KAAK,CAAC,OAAe;;YACjC,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC,CAAC,CAAA;QACpE,CAAC;KAAA;CACF;AAxDD,kCAwDC"}
+95
View File
@@ -0,0 +1,95 @@
import * as mm from './manifest';
export declare class HTTPError extends Error {
readonly httpStatusCode: number | undefined;
constructor(httpStatusCode: number | undefined);
}
/**
* Download a tool from an url and stream it into a file
*
* @param url url of tool to download
* @param dest path to download tool
* @param auth authorization header
* @returns path to downloaded tool
*/
export declare function downloadTool(url: string, dest?: string, auth?: string): Promise<string>;
/**
* Extract a .7z file
*
* @param file path to the .7z file
* @param dest destination directory. Optional.
* @param _7zPath path to 7zr.exe. Optional, for long path support. Most .7z archives do not have this
* problem. If your .7z archive contains very long paths, you can pass the path to 7zr.exe which will
* gracefully handle long paths. By default 7zdec.exe is used because it is a very small program and is
* bundled with the tool lib. However it does not support long paths. 7zr.exe is the reduced command line
* interface, it is smaller than the full command line interface, and it does support long paths. At the
* time of this writing, it is freely available from the LZMA SDK that is available on the 7zip website.
* Be sure to check the current license agreement. If 7zr.exe is bundled with your action, then the path
* to 7zr.exe can be pass to this function.
* @returns path to the destination directory
*/
export declare function extract7z(file: string, dest?: string, _7zPath?: string): Promise<string>;
/**
* Extract a compressed tar archive
*
* @param file path to the tar
* @param dest destination directory. Optional.
* @param flags flags for the tar command to use for extraction. Defaults to 'xz' (extracting gzipped tars). Optional.
* @returns path to the destination directory
*/
export declare function extractTar(file: string, dest?: string, flags?: string | string[]): Promise<string>;
/**
* Extract a xar compatible archive
*
* @param file path to the archive
* @param dest destination directory. Optional.
* @param flags flags for the xar. Optional.
* @returns path to the destination directory
*/
export declare function extractXar(file: string, dest?: string, flags?: string | string[]): Promise<string>;
/**
* Extract a zip
*
* @param file path to the zip
* @param dest destination directory. Optional.
* @returns path to the destination directory
*/
export declare function extractZip(file: string, dest?: string): Promise<string>;
/**
* Caches a directory and installs it into the tool cacheDir
*
* @param sourceDir the directory to cache into tools
* @param tool tool name
* @param version version of the tool. semver format
* @param arch architecture of the tool. Optional. Defaults to machine architecture
*/
export declare function cacheDir(sourceDir: string, tool: string, version: string, arch?: string): Promise<string>;
/**
* Caches a downloaded file (GUID) and installs it
* into the tool cache with a given targetName
*
* @param sourceFile the file to cache into tools. Typically a result of downloadTool which is a guid.
* @param targetFile the name of the file name in the tools directory
* @param tool tool name
* @param version version of the tool. semver format
* @param arch architecture of the tool. Optional. Defaults to machine architecture
*/
export declare function cacheFile(sourceFile: string, targetFile: string, tool: string, version: string, arch?: string): Promise<string>;
/**
* Finds the path to a tool version in the local installed tool cache
*
* @param toolName name of the tool
* @param versionSpec version of the tool
* @param arch optional arch. defaults to arch of computer
*/
export declare function find(toolName: string, versionSpec: string, arch?: string): string;
/**
* Finds the paths to all versions of a tool that are installed in the local tool cache
*
* @param toolName name of the tool
* @param arch optional arch. defaults to arch of computer
*/
export declare function findAllVersions(toolName: string, arch?: string): string[];
export declare type IToolRelease = mm.IToolRelease;
export declare type IToolReleaseFile = mm.IToolReleaseFile;
export declare function getManifestFromRepo(owner: string, repo: string, auth?: string, branch?: string): Promise<IToolRelease[]>;
export declare function findFromManifest(versionSpec: string, stable: boolean, manifest: IToolRelease[], archFilter?: string): Promise<IToolRelease | undefined>;
+606
View File
@@ -0,0 +1,606 @@
"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;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const core = __importStar(require("@actions/core"));
const io = __importStar(require("@actions/io"));
const fs = __importStar(require("fs"));
const mm = __importStar(require("./manifest"));
const os = __importStar(require("os"));
const path = __importStar(require("path"));
const httpm = __importStar(require("@actions/http-client"));
const semver = __importStar(require("semver"));
const stream = __importStar(require("stream"));
const util = __importStar(require("util"));
const v4_1 = __importDefault(require("uuid/v4"));
const exec_1 = require("@actions/exec/lib/exec");
const assert_1 = require("assert");
const retry_helper_1 = require("./retry-helper");
class HTTPError extends Error {
constructor(httpStatusCode) {
super(`Unexpected HTTP response: ${httpStatusCode}`);
this.httpStatusCode = httpStatusCode;
Object.setPrototypeOf(this, new.target.prototype);
}
}
exports.HTTPError = HTTPError;
const IS_WINDOWS = process.platform === 'win32';
const IS_MAC = process.platform === 'darwin';
const userAgent = 'actions/tool-cache';
/**
* Download a tool from an url and stream it into a file
*
* @param url url of tool to download
* @param dest path to download tool
* @param auth authorization header
* @returns path to downloaded tool
*/
function downloadTool(url, dest, auth) {
return __awaiter(this, void 0, void 0, function* () {
dest = dest || path.join(_getTempDirectory(), v4_1.default());
yield io.mkdirP(path.dirname(dest));
core.debug(`Downloading ${url}`);
core.debug(`Destination ${dest}`);
const maxAttempts = 3;
const minSeconds = _getGlobal('TEST_DOWNLOAD_TOOL_RETRY_MIN_SECONDS', 10);
const maxSeconds = _getGlobal('TEST_DOWNLOAD_TOOL_RETRY_MAX_SECONDS', 20);
const retryHelper = new retry_helper_1.RetryHelper(maxAttempts, minSeconds, maxSeconds);
return yield retryHelper.execute(() => __awaiter(this, void 0, void 0, function* () {
return yield downloadToolAttempt(url, dest || '', auth);
}), (err) => {
if (err instanceof HTTPError && err.httpStatusCode) {
// Don't retry anything less than 500, except 408 Request Timeout and 429 Too Many Requests
if (err.httpStatusCode < 500 &&
err.httpStatusCode !== 408 &&
err.httpStatusCode !== 429) {
return false;
}
}
// Otherwise retry
return true;
});
});
}
exports.downloadTool = downloadTool;
function downloadToolAttempt(url, dest, auth) {
return __awaiter(this, void 0, void 0, function* () {
if (fs.existsSync(dest)) {
throw new Error(`Destination file path ${dest} already exists`);
}
// Get the response headers
const http = new httpm.HttpClient(userAgent, [], {
allowRetries: false
});
let headers;
if (auth) {
core.debug('set auth');
headers = {
authorization: auth
};
}
const response = yield http.get(url, headers);
if (response.message.statusCode !== 200) {
const err = new HTTPError(response.message.statusCode);
core.debug(`Failed to download from "${url}". Code(${response.message.statusCode}) Message(${response.message.statusMessage})`);
throw err;
}
// Download the response body
const pipeline = util.promisify(stream.pipeline);
const responseMessageFactory = _getGlobal('TEST_DOWNLOAD_TOOL_RESPONSE_MESSAGE_FACTORY', () => response.message);
const readStream = responseMessageFactory();
let succeeded = false;
try {
yield pipeline(readStream, fs.createWriteStream(dest));
core.debug('download complete');
succeeded = true;
return dest;
}
finally {
// Error, delete dest before retry
if (!succeeded) {
core.debug('download failed');
try {
yield io.rmRF(dest);
}
catch (err) {
core.debug(`Failed to delete '${dest}'. ${err.message}`);
}
}
}
});
}
/**
* Extract a .7z file
*
* @param file path to the .7z file
* @param dest destination directory. Optional.
* @param _7zPath path to 7zr.exe. Optional, for long path support. Most .7z archives do not have this
* problem. If your .7z archive contains very long paths, you can pass the path to 7zr.exe which will
* gracefully handle long paths. By default 7zdec.exe is used because it is a very small program and is
* bundled with the tool lib. However it does not support long paths. 7zr.exe is the reduced command line
* interface, it is smaller than the full command line interface, and it does support long paths. At the
* time of this writing, it is freely available from the LZMA SDK that is available on the 7zip website.
* Be sure to check the current license agreement. If 7zr.exe is bundled with your action, then the path
* to 7zr.exe can be pass to this function.
* @returns path to the destination directory
*/
function extract7z(file, dest, _7zPath) {
return __awaiter(this, void 0, void 0, function* () {
assert_1.ok(IS_WINDOWS, 'extract7z() not supported on current OS');
assert_1.ok(file, 'parameter "file" is required');
dest = yield _createExtractFolder(dest);
const originalCwd = process.cwd();
process.chdir(dest);
if (_7zPath) {
try {
const logLevel = core.isDebug() ? '-bb1' : '-bb0';
const args = [
'x',
logLevel,
'-bd',
'-sccUTF-8',
file
];
const options = {
silent: true
};
yield exec_1.exec(`"${_7zPath}"`, args, options);
}
finally {
process.chdir(originalCwd);
}
}
else {
const escapedScript = path
.join(__dirname, '..', 'scripts', 'Invoke-7zdec.ps1')
.replace(/'/g, "''")
.replace(/"|\n|\r/g, ''); // double-up single quotes, remove double quotes and newlines
const escapedFile = file.replace(/'/g, "''").replace(/"|\n|\r/g, '');
const escapedTarget = dest.replace(/'/g, "''").replace(/"|\n|\r/g, '');
const command = `& '${escapedScript}' -Source '${escapedFile}' -Target '${escapedTarget}'`;
const args = [
'-NoLogo',
'-Sta',
'-NoProfile',
'-NonInteractive',
'-ExecutionPolicy',
'Unrestricted',
'-Command',
command
];
const options = {
silent: true
};
try {
const powershellPath = yield io.which('powershell', true);
yield exec_1.exec(`"${powershellPath}"`, args, options);
}
finally {
process.chdir(originalCwd);
}
}
return dest;
});
}
exports.extract7z = extract7z;
/**
* Extract a compressed tar archive
*
* @param file path to the tar
* @param dest destination directory. Optional.
* @param flags flags for the tar command to use for extraction. Defaults to 'xz' (extracting gzipped tars). Optional.
* @returns path to the destination directory
*/
function extractTar(file, dest, flags = 'xz') {
return __awaiter(this, void 0, void 0, function* () {
if (!file) {
throw new Error("parameter 'file' is required");
}
// Create dest
dest = yield _createExtractFolder(dest);
// Determine whether GNU tar
core.debug('Checking tar --version');
let versionOutput = '';
yield exec_1.exec('tar --version', [], {
ignoreReturnCode: true,
silent: true,
listeners: {
stdout: (data) => (versionOutput += data.toString()),
stderr: (data) => (versionOutput += data.toString())
}
});
core.debug(versionOutput.trim());
const isGnuTar = versionOutput.toUpperCase().includes('GNU TAR');
// Initialize args
let args;
if (flags instanceof Array) {
args = flags;
}
else {
args = [flags];
}
if (core.isDebug() && !flags.includes('v')) {
args.push('-v');
}
let destArg = dest;
let fileArg = file;
if (IS_WINDOWS && isGnuTar) {
args.push('--force-local');
destArg = dest.replace(/\\/g, '/');
// Technically only the dest needs to have `/` but for aesthetic consistency
// convert slashes in the file arg too.
fileArg = file.replace(/\\/g, '/');
}
if (isGnuTar) {
// Suppress warnings when using GNU tar to extract archives created by BSD tar
args.push('--warning=no-unknown-keyword');
}
args.push('-C', destArg, '-f', fileArg);
yield exec_1.exec(`tar`, args);
return dest;
});
}
exports.extractTar = extractTar;
/**
* Extract a xar compatible archive
*
* @param file path to the archive
* @param dest destination directory. Optional.
* @param flags flags for the xar. Optional.
* @returns path to the destination directory
*/
function extractXar(file, dest, flags = []) {
return __awaiter(this, void 0, void 0, function* () {
assert_1.ok(IS_MAC, 'extractXar() not supported on current OS');
assert_1.ok(file, 'parameter "file" is required');
dest = yield _createExtractFolder(dest);
let args;
if (flags instanceof Array) {
args = flags;
}
else {
args = [flags];
}
args.push('-x', '-C', dest, '-f', file);
if (core.isDebug()) {
args.push('-v');
}
const xarPath = yield io.which('xar', true);
yield exec_1.exec(`"${xarPath}"`, _unique(args));
return dest;
});
}
exports.extractXar = extractXar;
/**
* Extract a zip
*
* @param file path to the zip
* @param dest destination directory. Optional.
* @returns path to the destination directory
*/
function extractZip(file, dest) {
return __awaiter(this, void 0, void 0, function* () {
if (!file) {
throw new Error("parameter 'file' is required");
}
dest = yield _createExtractFolder(dest);
if (IS_WINDOWS) {
yield extractZipWin(file, dest);
}
else {
yield extractZipNix(file, dest);
}
return dest;
});
}
exports.extractZip = extractZip;
function extractZipWin(file, dest) {
return __awaiter(this, void 0, void 0, function* () {
// build the powershell command
const escapedFile = file.replace(/'/g, "''").replace(/"|\n|\r/g, ''); // double-up single quotes, remove double quotes and newlines
const escapedDest = dest.replace(/'/g, "''").replace(/"|\n|\r/g, '');
const command = `$ErrorActionPreference = 'Stop' ; try { Add-Type -AssemblyName System.IO.Compression.FileSystem } catch { } ; [System.IO.Compression.ZipFile]::ExtractToDirectory('${escapedFile}', '${escapedDest}')`;
// run powershell
const powershellPath = yield io.which('powershell', true);
const args = [
'-NoLogo',
'-Sta',
'-NoProfile',
'-NonInteractive',
'-ExecutionPolicy',
'Unrestricted',
'-Command',
command
];
yield exec_1.exec(`"${powershellPath}"`, args);
});
}
function extractZipNix(file, dest) {
return __awaiter(this, void 0, void 0, function* () {
const unzipPath = yield io.which('unzip', true);
const args = [file];
if (!core.isDebug()) {
args.unshift('-q');
}
yield exec_1.exec(`"${unzipPath}"`, args, { cwd: dest });
});
}
/**
* Caches a directory and installs it into the tool cacheDir
*
* @param sourceDir the directory to cache into tools
* @param tool tool name
* @param version version of the tool. semver format
* @param arch architecture of the tool. Optional. Defaults to machine architecture
*/
function cacheDir(sourceDir, tool, version, arch) {
return __awaiter(this, void 0, void 0, function* () {
version = semver.clean(version) || version;
arch = arch || os.arch();
core.debug(`Caching tool ${tool} ${version} ${arch}`);
core.debug(`source dir: ${sourceDir}`);
if (!fs.statSync(sourceDir).isDirectory()) {
throw new Error('sourceDir is not a directory');
}
// Create the tool dir
const destPath = yield _createToolPath(tool, version, arch);
// copy each child item. do not move. move can fail on Windows
// due to anti-virus software having an open handle on a file.
for (const itemName of fs.readdirSync(sourceDir)) {
const s = path.join(sourceDir, itemName);
yield io.cp(s, destPath, { recursive: true });
}
// write .complete
_completeToolPath(tool, version, arch);
return destPath;
});
}
exports.cacheDir = cacheDir;
/**
* Caches a downloaded file (GUID) and installs it
* into the tool cache with a given targetName
*
* @param sourceFile the file to cache into tools. Typically a result of downloadTool which is a guid.
* @param targetFile the name of the file name in the tools directory
* @param tool tool name
* @param version version of the tool. semver format
* @param arch architecture of the tool. Optional. Defaults to machine architecture
*/
function cacheFile(sourceFile, targetFile, tool, version, arch) {
return __awaiter(this, void 0, void 0, function* () {
version = semver.clean(version) || version;
arch = arch || os.arch();
core.debug(`Caching tool ${tool} ${version} ${arch}`);
core.debug(`source file: ${sourceFile}`);
if (!fs.statSync(sourceFile).isFile()) {
throw new Error('sourceFile is not a file');
}
// create the tool dir
const destFolder = yield _createToolPath(tool, version, arch);
// copy instead of move. move can fail on Windows due to
// anti-virus software having an open handle on a file.
const destPath = path.join(destFolder, targetFile);
core.debug(`destination file ${destPath}`);
yield io.cp(sourceFile, destPath);
// write .complete
_completeToolPath(tool, version, arch);
return destFolder;
});
}
exports.cacheFile = cacheFile;
/**
* Finds the path to a tool version in the local installed tool cache
*
* @param toolName name of the tool
* @param versionSpec version of the tool
* @param arch optional arch. defaults to arch of computer
*/
function find(toolName, versionSpec, arch) {
if (!toolName) {
throw new Error('toolName parameter is required');
}
if (!versionSpec) {
throw new Error('versionSpec parameter is required');
}
arch = arch || os.arch();
// attempt to resolve an explicit version
if (!_isExplicitVersion(versionSpec)) {
const localVersions = findAllVersions(toolName, arch);
const match = _evaluateVersions(localVersions, versionSpec);
versionSpec = match;
}
// check for the explicit version in the cache
let toolPath = '';
if (versionSpec) {
versionSpec = semver.clean(versionSpec) || '';
const cachePath = path.join(_getCacheDirectory(), toolName, versionSpec, arch);
core.debug(`checking cache: ${cachePath}`);
if (fs.existsSync(cachePath) && fs.existsSync(`${cachePath}.complete`)) {
core.debug(`Found tool in cache ${toolName} ${versionSpec} ${arch}`);
toolPath = cachePath;
}
else {
core.debug('not found');
}
}
return toolPath;
}
exports.find = find;
/**
* Finds the paths to all versions of a tool that are installed in the local tool cache
*
* @param toolName name of the tool
* @param arch optional arch. defaults to arch of computer
*/
function findAllVersions(toolName, arch) {
const versions = [];
arch = arch || os.arch();
const toolPath = path.join(_getCacheDirectory(), toolName);
if (fs.existsSync(toolPath)) {
const children = fs.readdirSync(toolPath);
for (const child of children) {
if (_isExplicitVersion(child)) {
const fullPath = path.join(toolPath, child, arch || '');
if (fs.existsSync(fullPath) && fs.existsSync(`${fullPath}.complete`)) {
versions.push(child);
}
}
}
}
return versions;
}
exports.findAllVersions = findAllVersions;
function getManifestFromRepo(owner, repo, auth, branch = 'master') {
return __awaiter(this, void 0, void 0, function* () {
let releases = [];
const treeUrl = `https://api.github.com/repos/${owner}/${repo}/git/trees/${branch}`;
const http = new httpm.HttpClient('tool-cache');
const headers = {};
if (auth) {
core.debug('set auth');
headers.authorization = auth;
}
const response = yield http.getJson(treeUrl, headers);
if (!response.result) {
return releases;
}
let manifestUrl = '';
for (const item of response.result.tree) {
if (item.path === 'versions-manifest.json') {
manifestUrl = item.url;
break;
}
}
headers['accept'] = 'application/vnd.github.VERSION.raw';
let versionsRaw = yield (yield http.get(manifestUrl, headers)).readBody();
if (versionsRaw) {
// shouldn't be needed but protects against invalid json saved with BOM
versionsRaw = versionsRaw.replace(/^\uFEFF/, '');
try {
releases = JSON.parse(versionsRaw);
}
catch (_a) {
core.debug('Invalid json');
}
}
return releases;
});
}
exports.getManifestFromRepo = getManifestFromRepo;
function findFromManifest(versionSpec, stable, manifest, archFilter = os.arch()) {
return __awaiter(this, void 0, void 0, function* () {
// wrap the internal impl
const match = yield mm._findMatch(versionSpec, stable, manifest, archFilter);
return match;
});
}
exports.findFromManifest = findFromManifest;
function _createExtractFolder(dest) {
return __awaiter(this, void 0, void 0, function* () {
if (!dest) {
// create a temp dir
dest = path.join(_getTempDirectory(), v4_1.default());
}
yield io.mkdirP(dest);
return dest;
});
}
function _createToolPath(tool, version, arch) {
return __awaiter(this, void 0, void 0, function* () {
const folderPath = path.join(_getCacheDirectory(), tool, semver.clean(version) || version, arch || '');
core.debug(`destination ${folderPath}`);
const markerPath = `${folderPath}.complete`;
yield io.rmRF(folderPath);
yield io.rmRF(markerPath);
yield io.mkdirP(folderPath);
return folderPath;
});
}
function _completeToolPath(tool, version, arch) {
const folderPath = path.join(_getCacheDirectory(), tool, semver.clean(version) || version, arch || '');
const markerPath = `${folderPath}.complete`;
fs.writeFileSync(markerPath, '');
core.debug('finished caching tool');
}
function _isExplicitVersion(versionSpec) {
const c = semver.clean(versionSpec) || '';
core.debug(`isExplicit: ${c}`);
const valid = semver.valid(c) != null;
core.debug(`explicit? ${valid}`);
return valid;
}
function _evaluateVersions(versions, versionSpec) {
let version = '';
core.debug(`evaluating ${versions.length} versions`);
versions = versions.sort((a, b) => {
if (semver.gt(a, b)) {
return 1;
}
return -1;
});
for (let i = versions.length - 1; i >= 0; i--) {
const potential = versions[i];
const satisfied = semver.satisfies(potential, versionSpec);
if (satisfied) {
version = potential;
break;
}
}
if (version) {
core.debug(`matched: ${version}`);
}
else {
core.debug('match not found');
}
return version;
}
/**
* Gets RUNNER_TOOL_CACHE
*/
function _getCacheDirectory() {
const cacheDirectory = process.env['RUNNER_TOOL_CACHE'] || '';
assert_1.ok(cacheDirectory, 'Expected RUNNER_TOOL_CACHE to be defined');
return cacheDirectory;
}
/**
* Gets RUNNER_TEMP
*/
function _getTempDirectory() {
const tempDirectory = process.env['RUNNER_TEMP'] || '';
assert_1.ok(tempDirectory, 'Expected RUNNER_TEMP to be defined');
return tempDirectory;
}
/**
* Gets a global variable
*/
function _getGlobal(key, defaultValue) {
/* eslint-disable @typescript-eslint/no-explicit-any */
const value = global[key];
/* eslint-enable @typescript-eslint/no-explicit-any */
return value !== undefined ? value : defaultValue;
}
/**
* Returns an array of unique values.
* @param values Values to make unique.
*/
function _unique(values) {
return Array.from(new Set(values));
}
//# sourceMappingURL=tool-cache.js.map
File diff suppressed because one or more lines are too long
+78
View File
@@ -0,0 +1,78 @@
{
"_from": "@actions/tool-cache@1.6.0",
"_id": "@actions/tool-cache@1.6.0",
"_inBundle": false,
"_integrity": "sha512-+fyEBImPD3m5I0o6DflCO0NHY180LPoX8Lo6y4Iez+V17kO8kfkH0VHxb8mUdmD6hn9dWA9Ch1JA20fXoIYUeQ==",
"_location": "/@actions/tool-cache",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "@actions/tool-cache@1.6.0",
"name": "@actions/tool-cache",
"escapedName": "@actions%2ftool-cache",
"scope": "@actions",
"rawSpec": "1.6.0",
"saveSpec": null,
"fetchSpec": "1.6.0"
},
"_requiredBy": [
"/"
],
"_resolved": "https://registry.npmjs.org/@actions/tool-cache/-/tool-cache-1.6.0.tgz",
"_shasum": "5b425db2d642df65dd0d6bcec0d84dcdbca3f80d",
"_spec": "@actions/tool-cache@1.6.0",
"_where": "/Users/aj/repos/k8s-deploy",
"bugs": {
"url": "https://github.com/actions/toolkit/issues"
},
"bundleDependencies": false,
"dependencies": {
"@actions/core": "^1.2.3",
"@actions/exec": "^1.0.0",
"@actions/http-client": "^1.0.8",
"@actions/io": "^1.0.1",
"semver": "^6.1.0",
"uuid": "^3.3.2"
},
"deprecated": false,
"description": "Actions tool-cache lib",
"devDependencies": {
"@types/nock": "^10.0.3",
"@types/semver": "^6.0.0",
"@types/uuid": "^3.4.4",
"nock": "^10.0.6"
},
"directories": {
"lib": "lib",
"test": "__tests__"
},
"files": [
"lib",
"scripts"
],
"homepage": "https://github.com/actions/toolkit/tree/master/packages/tool-cache",
"keywords": [
"github",
"actions",
"exec"
],
"license": "MIT",
"main": "lib/tool-cache.js",
"name": "@actions/tool-cache",
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "git+https://github.com/actions/toolkit.git",
"directory": "packages/tool-cache"
},
"scripts": {
"audit-moderate": "npm install && npm audit --json --audit-level=moderate > audit.json",
"test": "echo \"Error: run tests from root\" && exit 1",
"tsc": "tsc"
},
"types": "lib/tool-cache.d.ts",
"version": "1.6.0"
}
+60
View File
@@ -0,0 +1,60 @@
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$Source,
[Parameter(Mandatory = $true)]
[string]$Target)
# This script translates the output from 7zdec into UTF8. Node has limited
# built-in support for encodings.
#
# 7zdec uses the system default code page. The system default code page varies
# depending on the locale configuration. On an en-US box, the system default code
# page is Windows-1252.
#
# Note, on a typical en-US box, testing with the 'ç' character is a good way to
# determine whether data is passed correctly between processes. This is because
# the 'ç' character has a different code point across each of the common encodings
# on a typical en-US box, i.e.
# 1) the default console-output code page (IBM437)
# 2) the system default code page (i.e. CP_ACP) (Windows-1252)
# 3) UTF8
$ErrorActionPreference = 'Stop'
# Redefine the wrapper over STDOUT to use UTF8. Node expects UTF8 by default.
$stdout = [System.Console]::OpenStandardOutput()
$utf8 = New-Object System.Text.UTF8Encoding($false) # do not emit BOM
$writer = New-Object System.IO.StreamWriter($stdout, $utf8)
[System.Console]::SetOut($writer)
# All subsequent output must be written using [System.Console]::WriteLine(). In
# PowerShell 4, Write-Host and Out-Default do not consider the updated stream writer.
Set-Location -LiteralPath $Target
# Print the ##command.
$_7zdec = Join-Path -Path "$PSScriptRoot" -ChildPath "externals/7zdec.exe"
[System.Console]::WriteLine("##[command]$_7zdec x `"$Source`"")
# The $OutputEncoding variable instructs PowerShell how to interpret the output
# from the external command.
$OutputEncoding = [System.Text.Encoding]::Default
# Note, the output from 7zdec.exe needs to be iterated over. Otherwise PowerShell.exe
# will launch the external command in such a way that it inherits the streams.
& $_7zdec x $Source 2>&1 |
ForEach-Object {
if ($_ -is [System.Management.Automation.ErrorRecord]) {
[System.Console]::WriteLine($_.Exception.Message)
}
else {
[System.Console]::WriteLine($_)
}
}
[System.Console]::WriteLine("##[debug]7zdec.exe exit code '$LASTEXITCODE'")
[System.Console]::Out.Flush()
if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE
}
Binary file not shown.
+185
View File
@@ -0,0 +1,185 @@
1.0.10 / 2018-02-15
------------------
- Use .concat instead of + for arrays, #122.
1.0.9 / 2016-09-29
------------------
- Rerelease after 1.0.8 - deps cleanup.
1.0.8 / 2016-09-29
------------------
- Maintenance (deps bump, fix node 6.5+ tests, coverage report).
1.0.7 / 2016-03-17
------------------
- Teach `addArgument` to accept string arg names. #97, @tomxtobin.
1.0.6 / 2016-02-06
------------------
- Maintenance: moved to eslint & updated CS.
1.0.5 / 2016-02-05
------------------
- Removed lodash dependency to significantly reduce install size.
Thanks to @mourner.
1.0.4 / 2016-01-17
------------------
- Maintenance: lodash update to 4.0.0.
1.0.3 / 2015-10-27
------------------
- Fix parse `=` in args: `--examplepath="C:\myfolder\env=x64"`. #84, @CatWithApple.
1.0.2 / 2015-03-22
------------------
- Relaxed lodash version dependency.
1.0.1 / 2015-02-20
------------------
- Changed dependencies to be compatible with ancient nodejs.
1.0.0 / 2015-02-19
------------------
- Maintenance release.
- Replaced `underscore` with `lodash`.
- Bumped version to 1.0.0 to better reflect semver meaning.
- HISTORY.md -> CHANGELOG.md
0.1.16 / 2013-12-01
-------------------
- Maintenance release. Updated dependencies and docs.
0.1.15 / 2013-05-13
-------------------
- Fixed #55, @trebor89
0.1.14 / 2013-05-12
-------------------
- Fixed #62, @maxtaco
0.1.13 / 2013-04-08
-------------------
- Added `.npmignore` to reduce package size
0.1.12 / 2013-02-10
-------------------
- Fixed conflictHandler (#46), @hpaulj
0.1.11 / 2013-02-07
-------------------
- Multiple bugfixes, @hpaulj
- Added 70+ tests (ported from python), @hpaulj
- Added conflictHandler, @applepicke
- Added fromfilePrefixChar, @hpaulj
0.1.10 / 2012-12-30
-------------------
- Added [mutual exclusion](http://docs.python.org/dev/library/argparse.html#mutual-exclusion)
support, thanks to @hpaulj
- Fixed options check for `storeConst` & `appendConst` actions, thanks to @hpaulj
0.1.9 / 2012-12-27
------------------
- Fixed option dest interferens with other options (issue #23), thanks to @hpaulj
- Fixed default value behavior with `*` positionals, thanks to @hpaulj
- Improve `getDefault()` behavior, thanks to @hpaulj
- Imrove negative argument parsing, thanks to @hpaulj
0.1.8 / 2012-12-01
------------------
- Fixed parser parents (issue #19), thanks to @hpaulj
- Fixed negative argument parse (issue #20), thanks to @hpaulj
0.1.7 / 2012-10-14
------------------
- Fixed 'choices' argument parse (issue #16)
- Fixed stderr output (issue #15)
0.1.6 / 2012-09-09
------------------
- Fixed check for conflict of options (thanks to @tomxtobin)
0.1.5 / 2012-09-03
------------------
- Fix parser #setDefaults method (thanks to @tomxtobin)
0.1.4 / 2012-07-30
------------------
- Fixed pseudo-argument support (thanks to @CGamesPlay)
- Fixed addHelp default (should be true), if not set (thanks to @benblank)
0.1.3 / 2012-06-27
------------------
- Fixed formatter api name: Formatter -> HelpFormatter
0.1.2 / 2012-05-29
------------------
- Added basic tests
- Removed excess whitespace in help
- Fixed error reporting, when parcer with subcommands
called with empty arguments
0.1.1 / 2012-05-23
------------------
- Fixed line wrapping in help formatter
- Added better error reporting on invalid arguments
0.1.0 / 2012-05-16
------------------
- First release.
+21
View File
@@ -0,0 +1,21 @@
(The MIT License)
Copyright (C) 2012 by Vitaly Puzrin
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.
+257
View File
@@ -0,0 +1,257 @@
argparse
========
[![Build Status](https://secure.travis-ci.org/nodeca/argparse.svg?branch=master)](http://travis-ci.org/nodeca/argparse)
[![NPM version](https://img.shields.io/npm/v/argparse.svg)](https://www.npmjs.org/package/argparse)
CLI arguments parser for node.js. Javascript port of python's
[argparse](http://docs.python.org/dev/library/argparse.html) module
(original version 3.2). That's a full port, except some very rare options,
recorded in issue tracker.
**NB. Difference with original.**
- Method names changed to camelCase. See [generated docs](http://nodeca.github.com/argparse/).
- Use `defaultValue` instead of `default`.
- Use `argparse.Const.REMAINDER` instead of `argparse.REMAINDER`, and
similarly for constant values `OPTIONAL`, `ZERO_OR_MORE`, and `ONE_OR_MORE`
(aliases for `nargs` values `'?'`, `'*'`, `'+'`, respectively), and
`SUPPRESS`.
Example
=======
test.js file:
```javascript
#!/usr/bin/env node
'use strict';
var ArgumentParser = require('../lib/argparse').ArgumentParser;
var parser = new ArgumentParser({
version: '0.0.1',
addHelp:true,
description: 'Argparse example'
});
parser.addArgument(
[ '-f', '--foo' ],
{
help: 'foo bar'
}
);
parser.addArgument(
[ '-b', '--bar' ],
{
help: 'bar foo'
}
);
parser.addArgument(
'--baz',
{
help: 'baz bar'
}
);
var args = parser.parseArgs();
console.dir(args);
```
Display help:
```
$ ./test.js -h
usage: example.js [-h] [-v] [-f FOO] [-b BAR] [--baz BAZ]
Argparse example
Optional arguments:
-h, --help Show this help message and exit.
-v, --version Show program's version number and exit.
-f FOO, --foo FOO foo bar
-b BAR, --bar BAR bar foo
--baz BAZ baz bar
```
Parse arguments:
```
$ ./test.js -f=3 --bar=4 --baz 5
{ foo: '3', bar: '4', baz: '5' }
```
More [examples](https://github.com/nodeca/argparse/tree/master/examples).
ArgumentParser objects
======================
```
new ArgumentParser({parameters hash});
```
Creates a new ArgumentParser object.
**Supported params:**
- ```description``` - Text to display before the argument help.
- ```epilog``` - Text to display after the argument help.
- ```addHelp``` - Add a -h/help option to the parser. (default: true)
- ```argumentDefault``` - Set the global default value for arguments. (default: null)
- ```parents``` - A list of ArgumentParser objects whose arguments should also be included.
- ```prefixChars``` - The set of characters that prefix optional arguments. (default: -)
- ```formatterClass``` - A class for customizing the help output.
- ```prog``` - The name of the program (default: `path.basename(process.argv[1])`)
- ```usage``` - The string describing the program usage (default: generated)
- ```conflictHandler``` - Usually unnecessary, defines strategy for resolving conflicting optionals.
**Not supported yet**
- ```fromfilePrefixChars``` - The set of characters that prefix files from which additional arguments should be read.
Details in [original ArgumentParser guide](http://docs.python.org/dev/library/argparse.html#argumentparser-objects)
addArgument() method
====================
```
ArgumentParser.addArgument(name or flag or [name] or [flags...], {options})
```
Defines how a single command-line argument should be parsed.
- ```name or flag or [name] or [flags...]``` - Either a positional name
(e.g., `'foo'`), a single option (e.g., `'-f'` or `'--foo'`), an array
of a single positional name (e.g., `['foo']`), or an array of options
(e.g., `['-f', '--foo']`).
Options:
- ```action``` - The basic type of action to be taken when this argument is encountered at the command line.
- ```nargs```- The number of command-line arguments that should be consumed.
- ```constant``` - A constant value required by some action and nargs selections.
- ```defaultValue``` - The value produced if the argument is absent from the command line.
- ```type``` - The type to which the command-line argument should be converted.
- ```choices``` - A container of the allowable values for the argument.
- ```required``` - Whether or not the command-line option may be omitted (optionals only).
- ```help``` - A brief description of what the argument does.
- ```metavar``` - A name for the argument in usage messages.
- ```dest``` - The name of the attribute to be added to the object returned by parseArgs().
Details in [original add_argument guide](http://docs.python.org/dev/library/argparse.html#the-add-argument-method)
Action (some details)
================
ArgumentParser objects associate command-line arguments with actions.
These actions can do just about anything with the command-line arguments associated
with them, though most actions simply add an attribute to the object returned by
parseArgs(). The action keyword argument specifies how the command-line arguments
should be handled. The supported actions are:
- ```store``` - Just stores the arguments value. This is the default action.
- ```storeConst``` - Stores value, specified by the const keyword argument.
(Note that the const keyword argument defaults to the rather unhelpful None.)
The 'storeConst' action is most commonly used with optional arguments, that
specify some sort of flag.
- ```storeTrue``` and ```storeFalse``` - Stores values True and False
respectively. These are special cases of 'storeConst'.
- ```append``` - Stores a list, and appends each argument value to the list.
This is useful to allow an option to be specified multiple times.
- ```appendConst``` - Stores a list, and appends value, specified by the
const keyword argument to the list. (Note, that the const keyword argument defaults
is None.) The 'appendConst' action is typically used when multiple arguments need
to store constants to the same list.
- ```count``` - Counts the number of times a keyword argument occurs. For example,
used for increasing verbosity levels.
- ```help``` - Prints a complete help message for all the options in the current
parser and then exits. By default a help action is automatically added to the parser.
See ArgumentParser for details of how the output is created.
- ```version``` - Prints version information and exit. Expects a `version=`
keyword argument in the addArgument() call.
Details in [original action guide](http://docs.python.org/dev/library/argparse.html#action)
Sub-commands
============
ArgumentParser.addSubparsers()
Many programs split their functionality into a number of sub-commands, for
example, the svn program can invoke sub-commands like `svn checkout`, `svn update`,
and `svn commit`. Splitting up functionality this way can be a particularly good
idea when a program performs several different functions which require different
kinds of command-line arguments. `ArgumentParser` supports creation of such
sub-commands with `addSubparsers()` method. The `addSubparsers()` method is
normally called with no arguments and returns an special action object.
This object has a single method `addParser()`, which takes a command name and
any `ArgumentParser` constructor arguments, and returns an `ArgumentParser` object
that can be modified as usual.
Example:
sub_commands.js
```javascript
#!/usr/bin/env node
'use strict';
var ArgumentParser = require('../lib/argparse').ArgumentParser;
var parser = new ArgumentParser({
version: '0.0.1',
addHelp:true,
description: 'Argparse examples: sub-commands',
});
var subparsers = parser.addSubparsers({
title:'subcommands',
dest:"subcommand_name"
});
var bar = subparsers.addParser('c1', {addHelp:true});
bar.addArgument(
[ '-f', '--foo' ],
{
action: 'store',
help: 'foo3 bar3'
}
);
var bar = subparsers.addParser(
'c2',
{aliases:['co'], addHelp:true}
);
bar.addArgument(
[ '-b', '--bar' ],
{
action: 'store',
type: 'int',
help: 'foo3 bar3'
}
);
var args = parser.parseArgs();
console.dir(args);
```
Details in [original sub-commands guide](http://docs.python.org/dev/library/argparse.html#sub-commands)
Contributors
============
- [Eugene Shkuropat](https://github.com/shkuropat)
- [Paul Jacobson](https://github.com/hpaulj)
[others](https://github.com/nodeca/argparse/graphs/contributors)
License
=======
Copyright (c) 2012 [Vitaly Puzrin](https://github.com/puzrin).
Released under the MIT license. See
[LICENSE](https://github.com/nodeca/argparse/blob/master/LICENSE) for details.
+3
View File
@@ -0,0 +1,3 @@
'use strict';
module.exports = require('./lib/argparse');
+146
View File
@@ -0,0 +1,146 @@
/**
* class Action
*
* Base class for all actions
* Do not call in your code, use this class only for inherits your own action
*
* Information about how to convert command line strings to Javascript objects.
* Action objects are used by an ArgumentParser to represent the information
* needed to parse a single argument from one or more strings from the command
* line. The keyword arguments to the Action constructor are also all attributes
* of Action instances.
*
* ##### Allowed keywords:
*
* - `store`
* - `storeConstant`
* - `storeTrue`
* - `storeFalse`
* - `append`
* - `appendConstant`
* - `count`
* - `help`
* - `version`
*
* Information about action options see [[Action.new]]
*
* See also [original guide](http://docs.python.org/dev/library/argparse.html#action)
*
**/
'use strict';
// Constants
var c = require('./const');
/**
* new Action(options)
*
* Base class for all actions. Used only for inherits
*
*
* ##### Options:
*
* - `optionStrings` A list of command-line option strings for the action.
* - `dest` Attribute to hold the created object(s)
* - `nargs` The number of command-line arguments that should be consumed.
* By default, one argument will be consumed and a single value will be
* produced.
* - `constant` Default value for an action with no value.
* - `defaultValue` The value to be produced if the option is not specified.
* - `type` Cast to 'string'|'int'|'float'|'complex'|function (string). If
* None, 'string'.
* - `choices` The choices available.
* - `required` True if the action must always be specified at the command
* line.
* - `help` The help describing the argument.
* - `metavar` The name to be used for the option's argument with the help
* string. If None, the 'dest' value will be used as the name.
*
* ##### nargs supported values:
*
* - `N` (an integer) consumes N arguments (and produces a list)
* - `?` consumes zero or one arguments
* - `*` consumes zero or more arguments (and produces a list)
* - `+` consumes one or more arguments (and produces a list)
*
* Note: that the difference between the default and nargs=1 is that with the
* default, a single value will be produced, while with nargs=1, a list
* containing a single value will be produced.
**/
var Action = module.exports = function Action(options) {
options = options || {};
this.optionStrings = options.optionStrings || [];
this.dest = options.dest;
this.nargs = typeof options.nargs !== 'undefined' ? options.nargs : null;
this.constant = typeof options.constant !== 'undefined' ? options.constant : null;
this.defaultValue = options.defaultValue;
this.type = typeof options.type !== 'undefined' ? options.type : null;
this.choices = typeof options.choices !== 'undefined' ? options.choices : null;
this.required = typeof options.required !== 'undefined' ? options.required : false;
this.help = typeof options.help !== 'undefined' ? options.help : null;
this.metavar = typeof options.metavar !== 'undefined' ? options.metavar : null;
if (!(this.optionStrings instanceof Array)) {
throw new Error('optionStrings should be an array');
}
if (typeof this.required !== 'undefined' && typeof this.required !== 'boolean') {
throw new Error('required should be a boolean');
}
};
/**
* Action#getName -> String
*
* Tells action name
**/
Action.prototype.getName = function () {
if (this.optionStrings.length > 0) {
return this.optionStrings.join('/');
} else if (this.metavar !== null && this.metavar !== c.SUPPRESS) {
return this.metavar;
} else if (typeof this.dest !== 'undefined' && this.dest !== c.SUPPRESS) {
return this.dest;
}
return null;
};
/**
* Action#isOptional -> Boolean
*
* Return true if optional
**/
Action.prototype.isOptional = function () {
return !this.isPositional();
};
/**
* Action#isPositional -> Boolean
*
* Return true if positional
**/
Action.prototype.isPositional = function () {
return (this.optionStrings.length === 0);
};
/**
* Action#call(parser, namespace, values, optionString) -> Void
* - parser (ArgumentParser): current parser
* - namespace (Namespace): namespace for output data
* - values (Array): parsed values
* - optionString (Array): input option string(not parsed)
*
* Call the action. Should be implemented in inherited classes
*
* ##### Example
*
* ActionCount.prototype.call = function (parser, namespace, values, optionString) {
* namespace.set(this.dest, (namespace[this.dest] || 0) + 1);
* };
*
**/
Action.prototype.call = function () {
throw new Error('.call() not defined');// Not Implemented error
};
+53
View File
@@ -0,0 +1,53 @@
/*:nodoc:*
* class ActionAppend
*
* This action stores a list, and appends each argument value to the list.
* This is useful to allow an option to be specified multiple times.
* This class inherided from [[Action]]
*
**/
'use strict';
var util = require('util');
var Action = require('../action');
// Constants
var c = require('../const');
/*:nodoc:*
* new ActionAppend(options)
* - options (object): options hash see [[Action.new]]
*
* Note: options.nargs should be optional for constants
* and more then zero for other
**/
var ActionAppend = module.exports = function ActionAppend(options) {
options = options || {};
if (this.nargs <= 0) {
throw new Error('nargs for append actions must be > 0; if arg ' +
'strings are not supplying the value to append, ' +
'the append const action may be more appropriate');
}
if (!!this.constant && this.nargs !== c.OPTIONAL) {
throw new Error('nargs must be OPTIONAL to supply const');
}
Action.call(this, options);
};
util.inherits(ActionAppend, Action);
/*:nodoc:*
* ActionAppend#call(parser, namespace, values, optionString) -> Void
* - parser (ArgumentParser): current parser
* - namespace (Namespace): namespace for output data
* - values (Array): parsed values
* - optionString (Array): input option string(not parsed)
*
* Call the action. Save result in namespace object
**/
ActionAppend.prototype.call = function (parser, namespace, values) {
var items = (namespace[this.dest] || []).slice();
items.push(values);
namespace.set(this.dest, items);
};
+47
View File
@@ -0,0 +1,47 @@
/*:nodoc:*
* class ActionAppendConstant
*
* This stores a list, and appends the value specified by
* the const keyword argument to the list.
* (Note that the const keyword argument defaults to null.)
* The 'appendConst' action is typically useful when multiple
* arguments need to store constants to the same list.
*
* This class inherited from [[Action]]
**/
'use strict';
var util = require('util');
var Action = require('../../action');
/*:nodoc:*
* new ActionAppendConstant(options)
* - options (object): options hash see [[Action.new]]
*
**/
var ActionAppendConstant = module.exports = function ActionAppendConstant(options) {
options = options || {};
options.nargs = 0;
if (typeof options.constant === 'undefined') {
throw new Error('constant option is required for appendAction');
}
Action.call(this, options);
};
util.inherits(ActionAppendConstant, Action);
/*:nodoc:*
* ActionAppendConstant#call(parser, namespace, values, optionString) -> Void
* - parser (ArgumentParser): current parser
* - namespace (Namespace): namespace for output data
* - values (Array): parsed values
* - optionString (Array): input option string(not parsed)
*
* Call the action. Save result in namespace object
**/
ActionAppendConstant.prototype.call = function (parser, namespace) {
var items = [].concat(namespace[this.dest] || []);
items.push(this.constant);
namespace.set(this.dest, items);
};
+40
View File
@@ -0,0 +1,40 @@
/*:nodoc:*
* class ActionCount
*
* This counts the number of times a keyword argument occurs.
* For example, this is useful for increasing verbosity levels
*
* This class inherided from [[Action]]
*
**/
'use strict';
var util = require('util');
var Action = require('../action');
/*:nodoc:*
* new ActionCount(options)
* - options (object): options hash see [[Action.new]]
*
**/
var ActionCount = module.exports = function ActionCount(options) {
options = options || {};
options.nargs = 0;
Action.call(this, options);
};
util.inherits(ActionCount, Action);
/*:nodoc:*
* ActionCount#call(parser, namespace, values, optionString) -> Void
* - parser (ArgumentParser): current parser
* - namespace (Namespace): namespace for output data
* - values (Array): parsed values
* - optionString (Array): input option string(not parsed)
*
* Call the action. Save result in namespace object
**/
ActionCount.prototype.call = function (parser, namespace) {
namespace.set(this.dest, (namespace[this.dest] || 0) + 1);
};
+47
View File
@@ -0,0 +1,47 @@
/*:nodoc:*
* class ActionHelp
*
* Support action for printing help
* This class inherided from [[Action]]
**/
'use strict';
var util = require('util');
var Action = require('../action');
// Constants
var c = require('../const');
/*:nodoc:*
* new ActionHelp(options)
* - options (object): options hash see [[Action.new]]
*
**/
var ActionHelp = module.exports = function ActionHelp(options) {
options = options || {};
if (options.defaultValue !== null) {
options.defaultValue = options.defaultValue;
} else {
options.defaultValue = c.SUPPRESS;
}
options.dest = (options.dest !== null ? options.dest : c.SUPPRESS);
options.nargs = 0;
Action.call(this, options);
};
util.inherits(ActionHelp, Action);
/*:nodoc:*
* ActionHelp#call(parser, namespace, values, optionString)
* - parser (ArgumentParser): current parser
* - namespace (Namespace): namespace for output data
* - values (Array): parsed values
* - optionString (Array): input option string(not parsed)
*
* Print help and exit
**/
ActionHelp.prototype.call = function (parser) {
parser.printHelp();
parser.exit();
};
+50
View File
@@ -0,0 +1,50 @@
/*:nodoc:*
* class ActionStore
*
* This action just stores the arguments value. This is the default action.
*
* This class inherited from [[Action]]
*
**/
'use strict';
var util = require('util');
var Action = require('../action');
// Constants
var c = require('../const');
/*:nodoc:*
* new ActionStore(options)
* - options (object): options hash see [[Action.new]]
*
**/
var ActionStore = module.exports = function ActionStore(options) {
options = options || {};
if (this.nargs <= 0) {
throw new Error('nargs for store actions must be > 0; if you ' +
'have nothing to store, actions such as store ' +
'true or store const may be more appropriate');
}
if (typeof this.constant !== 'undefined' && this.nargs !== c.OPTIONAL) {
throw new Error('nargs must be OPTIONAL to supply const');
}
Action.call(this, options);
};
util.inherits(ActionStore, Action);
/*:nodoc:*
* ActionStore#call(parser, namespace, values, optionString) -> Void
* - parser (ArgumentParser): current parser
* - namespace (Namespace): namespace for output data
* - values (Array): parsed values
* - optionString (Array): input option string(not parsed)
*
* Call the action. Save result in namespace object
**/
ActionStore.prototype.call = function (parser, namespace, values) {
namespace.set(this.dest, values);
};
+43
View File
@@ -0,0 +1,43 @@
/*:nodoc:*
* class ActionStoreConstant
*
* This action stores the value specified by the const keyword argument.
* (Note that the const keyword argument defaults to the rather unhelpful null.)
* The 'store_const' action is most commonly used with optional
* arguments that specify some sort of flag.
*
* This class inherited from [[Action]]
**/
'use strict';
var util = require('util');
var Action = require('../../action');
/*:nodoc:*
* new ActionStoreConstant(options)
* - options (object): options hash see [[Action.new]]
*
**/
var ActionStoreConstant = module.exports = function ActionStoreConstant(options) {
options = options || {};
options.nargs = 0;
if (typeof options.constant === 'undefined') {
throw new Error('constant option is required for storeAction');
}
Action.call(this, options);
};
util.inherits(ActionStoreConstant, Action);
/*:nodoc:*
* ActionStoreConstant#call(parser, namespace, values, optionString) -> Void
* - parser (ArgumentParser): current parser
* - namespace (Namespace): namespace for output data
* - values (Array): parsed values
* - optionString (Array): input option string(not parsed)
*
* Call the action. Save result in namespace object
**/
ActionStoreConstant.prototype.call = function (parser, namespace) {
namespace.set(this.dest, this.constant);
};
+27
View File
@@ -0,0 +1,27 @@
/*:nodoc:*
* class ActionStoreFalse
*
* This action store the values False respectively.
* This is special cases of 'storeConst'
*
* This class inherited from [[Action]]
**/
'use strict';
var util = require('util');
var ActionStoreConstant = require('./constant');
/*:nodoc:*
* new ActionStoreFalse(options)
* - options (object): hash of options see [[Action.new]]
*
**/
var ActionStoreFalse = module.exports = function ActionStoreFalse(options) {
options = options || {};
options.constant = false;
options.defaultValue = options.defaultValue !== null ? options.defaultValue : true;
ActionStoreConstant.call(this, options);
};
util.inherits(ActionStoreFalse, ActionStoreConstant);
+26
View File
@@ -0,0 +1,26 @@
/*:nodoc:*
* class ActionStoreTrue
*
* This action store the values True respectively.
* This isspecial cases of 'storeConst'
*
* This class inherited from [[Action]]
**/
'use strict';
var util = require('util');
var ActionStoreConstant = require('./constant');
/*:nodoc:*
* new ActionStoreTrue(options)
* - options (object): options hash see [[Action.new]]
*
**/
var ActionStoreTrue = module.exports = function ActionStoreTrue(options) {
options = options || {};
options.constant = true;
options.defaultValue = options.defaultValue !== null ? options.defaultValue : false;
ActionStoreConstant.call(this, options);
};
util.inherits(ActionStoreTrue, ActionStoreConstant);
+149
View File
@@ -0,0 +1,149 @@
/** internal
* class ActionSubparsers
*
* Support the creation of such sub-commands with the addSubparsers()
*
* This class inherited from [[Action]]
**/
'use strict';
var util = require('util');
var format = require('util').format;
var Action = require('../action');
// Constants
var c = require('../const');
// Errors
var argumentErrorHelper = require('../argument/error');
/*:nodoc:*
* new ChoicesPseudoAction(name, help)
*
* Create pseudo action for correct help text
*
**/
function ChoicesPseudoAction(name, help) {
var options = {
optionStrings: [],
dest: name,
help: help
};
Action.call(this, options);
}
util.inherits(ChoicesPseudoAction, Action);
/**
* new ActionSubparsers(options)
* - options (object): options hash see [[Action.new]]
*
**/
function ActionSubparsers(options) {
options = options || {};
options.dest = options.dest || c.SUPPRESS;
options.nargs = c.PARSER;
this.debug = (options.debug === true);
this._progPrefix = options.prog;
this._parserClass = options.parserClass;
this._nameParserMap = {};
this._choicesActions = [];
options.choices = this._nameParserMap;
Action.call(this, options);
}
util.inherits(ActionSubparsers, Action);
/*:nodoc:*
* ActionSubparsers#addParser(name, options) -> ArgumentParser
* - name (string): sub-command name
* - options (object): see [[ArgumentParser.new]]
*
* Note:
* addParser supports an additional aliases option,
* which allows multiple strings to refer to the same subparser.
* This example, like svn, aliases co as a shorthand for checkout
*
**/
ActionSubparsers.prototype.addParser = function (name, options) {
var parser;
var self = this;
options = options || {};
options.debug = (this.debug === true);
// set program from the existing prefix
if (!options.prog) {
options.prog = this._progPrefix + ' ' + name;
}
var aliases = options.aliases || [];
// create a pseudo-action to hold the choice help
if (!!options.help || typeof options.help === 'string') {
var help = options.help;
delete options.help;
var choiceAction = new ChoicesPseudoAction(name, help);
this._choicesActions.push(choiceAction);
}
// create the parser and add it to the map
parser = new this._parserClass(options);
this._nameParserMap[name] = parser;
// make parser available under aliases also
aliases.forEach(function (alias) {
self._nameParserMap[alias] = parser;
});
return parser;
};
ActionSubparsers.prototype._getSubactions = function () {
return this._choicesActions;
};
/*:nodoc:*
* ActionSubparsers#call(parser, namespace, values, optionString) -> Void
* - parser (ArgumentParser): current parser
* - namespace (Namespace): namespace for output data
* - values (Array): parsed values
* - optionString (Array): input option string(not parsed)
*
* Call the action. Parse input aguments
**/
ActionSubparsers.prototype.call = function (parser, namespace, values) {
var parserName = values[0];
var argStrings = values.slice(1);
// set the parser name if requested
if (this.dest !== c.SUPPRESS) {
namespace[this.dest] = parserName;
}
// select the parser
if (this._nameParserMap[parserName]) {
parser = this._nameParserMap[parserName];
} else {
throw argumentErrorHelper(format(
'Unknown parser "%s" (choices: [%s]).',
parserName,
Object.keys(this._nameParserMap).join(', ')
));
}
// parse all the remaining options into the namespace
parser.parseArgs(argStrings, namespace);
};
module.exports = ActionSubparsers;
+47
View File
@@ -0,0 +1,47 @@
/*:nodoc:*
* class ActionVersion
*
* Support action for printing program version
* This class inherited from [[Action]]
**/
'use strict';
var util = require('util');
var Action = require('../action');
//
// Constants
//
var c = require('../const');
/*:nodoc:*
* new ActionVersion(options)
* - options (object): options hash see [[Action.new]]
*
**/
var ActionVersion = module.exports = function ActionVersion(options) {
options = options || {};
options.defaultValue = (options.defaultValue ? options.defaultValue : c.SUPPRESS);
options.dest = (options.dest || c.SUPPRESS);
options.nargs = 0;
this.version = options.version;
Action.call(this, options);
};
util.inherits(ActionVersion, Action);
/*:nodoc:*
* ActionVersion#call(parser, namespace, values, optionString) -> Void
* - parser (ArgumentParser): current parser
* - namespace (Namespace): namespace for output data
* - values (Array): parsed values
* - optionString (Array): input option string(not parsed)
*
* Print version and exit
**/
ActionVersion.prototype.call = function (parser) {
var version = this.version || parser.version;
var formatter = parser._getFormatter();
formatter.addText(version);
parser.exit(0, formatter.formatHelp());
};
+482
View File
@@ -0,0 +1,482 @@
/** internal
* class ActionContainer
*
* Action container. Parent for [[ArgumentParser]] and [[ArgumentGroup]]
**/
'use strict';
var format = require('util').format;
// Constants
var c = require('./const');
var $$ = require('./utils');
//Actions
var ActionHelp = require('./action/help');
var ActionAppend = require('./action/append');
var ActionAppendConstant = require('./action/append/constant');
var ActionCount = require('./action/count');
var ActionStore = require('./action/store');
var ActionStoreConstant = require('./action/store/constant');
var ActionStoreTrue = require('./action/store/true');
var ActionStoreFalse = require('./action/store/false');
var ActionVersion = require('./action/version');
var ActionSubparsers = require('./action/subparsers');
// Errors
var argumentErrorHelper = require('./argument/error');
/**
* new ActionContainer(options)
*
* Action container. Parent for [[ArgumentParser]] and [[ArgumentGroup]]
*
* ##### Options:
*
* - `description` -- A description of what the program does
* - `prefixChars` -- Characters that prefix optional arguments
* - `argumentDefault` -- The default value for all arguments
* - `conflictHandler` -- The conflict handler to use for duplicate arguments
**/
var ActionContainer = module.exports = function ActionContainer(options) {
options = options || {};
this.description = options.description;
this.argumentDefault = options.argumentDefault;
this.prefixChars = options.prefixChars || '';
this.conflictHandler = options.conflictHandler;
// set up registries
this._registries = {};
// register actions
this.register('action', null, ActionStore);
this.register('action', 'store', ActionStore);
this.register('action', 'storeConst', ActionStoreConstant);
this.register('action', 'storeTrue', ActionStoreTrue);
this.register('action', 'storeFalse', ActionStoreFalse);
this.register('action', 'append', ActionAppend);
this.register('action', 'appendConst', ActionAppendConstant);
this.register('action', 'count', ActionCount);
this.register('action', 'help', ActionHelp);
this.register('action', 'version', ActionVersion);
this.register('action', 'parsers', ActionSubparsers);
// raise an exception if the conflict handler is invalid
this._getHandler();
// action storage
this._actions = [];
this._optionStringActions = {};
// groups
this._actionGroups = [];
this._mutuallyExclusiveGroups = [];
// defaults storage
this._defaults = {};
// determines whether an "option" looks like a negative number
// -1, -1.5 -5e+4
this._regexpNegativeNumber = new RegExp('^[-]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?$');
// whether or not there are any optionals that look like negative
// numbers -- uses a list so it can be shared and edited
this._hasNegativeNumberOptionals = [];
};
// Groups must be required, then ActionContainer already defined
var ArgumentGroup = require('./argument/group');
var MutuallyExclusiveGroup = require('./argument/exclusive');
//
// Registration methods
//
/**
* ActionContainer#register(registryName, value, object) -> Void
* - registryName (String) : object type action|type
* - value (string) : keyword
* - object (Object|Function) : handler
*
* Register handlers
**/
ActionContainer.prototype.register = function (registryName, value, object) {
this._registries[registryName] = this._registries[registryName] || {};
this._registries[registryName][value] = object;
};
ActionContainer.prototype._registryGet = function (registryName, value, defaultValue) {
if (arguments.length < 3) {
defaultValue = null;
}
return this._registries[registryName][value] || defaultValue;
};
//
// Namespace default accessor methods
//
/**
* ActionContainer#setDefaults(options) -> Void
* - options (object):hash of options see [[Action.new]]
*
* Set defaults
**/
ActionContainer.prototype.setDefaults = function (options) {
options = options || {};
for (var property in options) {
if ($$.has(options, property)) {
this._defaults[property] = options[property];
}
}
// if these defaults match any existing arguments, replace the previous
// default on the object with the new one
this._actions.forEach(function (action) {
if ($$.has(options, action.dest)) {
action.defaultValue = options[action.dest];
}
});
};
/**
* ActionContainer#getDefault(dest) -> Mixed
* - dest (string): action destination
*
* Return action default value
**/
ActionContainer.prototype.getDefault = function (dest) {
var result = $$.has(this._defaults, dest) ? this._defaults[dest] : null;
this._actions.forEach(function (action) {
if (action.dest === dest && $$.has(action, 'defaultValue')) {
result = action.defaultValue;
}
});
return result;
};
//
// Adding argument actions
//
/**
* ActionContainer#addArgument(args, options) -> Object
* - args (String|Array): argument key, or array of argument keys
* - options (Object): action objects see [[Action.new]]
*
* #### Examples
* - addArgument([ '-f', '--foo' ], { action: 'store', defaultValue: 1, ... })
* - addArgument([ 'bar' ], { action: 'store', nargs: 1, ... })
* - addArgument('--baz', { action: 'store', nargs: 1, ... })
**/
ActionContainer.prototype.addArgument = function (args, options) {
args = args;
options = options || {};
if (typeof args === 'string') {
args = [ args ];
}
if (!Array.isArray(args)) {
throw new TypeError('addArgument first argument should be a string or an array');
}
if (typeof options !== 'object' || Array.isArray(options)) {
throw new TypeError('addArgument second argument should be a hash');
}
// if no positional args are supplied or only one is supplied and
// it doesn't look like an option string, parse a positional argument
if (!args || args.length === 1 && this.prefixChars.indexOf(args[0][0]) < 0) {
if (args && !!options.dest) {
throw new Error('dest supplied twice for positional argument');
}
options = this._getPositional(args, options);
// otherwise, we're adding an optional argument
} else {
options = this._getOptional(args, options);
}
// if no default was supplied, use the parser-level default
if (typeof options.defaultValue === 'undefined') {
var dest = options.dest;
if ($$.has(this._defaults, dest)) {
options.defaultValue = this._defaults[dest];
} else if (typeof this.argumentDefault !== 'undefined') {
options.defaultValue = this.argumentDefault;
}
}
// create the action object, and add it to the parser
var ActionClass = this._popActionClass(options);
if (typeof ActionClass !== 'function') {
throw new Error(format('Unknown action "%s".', ActionClass));
}
var action = new ActionClass(options);
// throw an error if the action type is not callable
var typeFunction = this._registryGet('type', action.type, action.type);
if (typeof typeFunction !== 'function') {
throw new Error(format('"%s" is not callable', typeFunction));
}
return this._addAction(action);
};
/**
* ActionContainer#addArgumentGroup(options) -> ArgumentGroup
* - options (Object): hash of options see [[ArgumentGroup.new]]
*
* Create new arguments groups
**/
ActionContainer.prototype.addArgumentGroup = function (options) {
var group = new ArgumentGroup(this, options);
this._actionGroups.push(group);
return group;
};
/**
* ActionContainer#addMutuallyExclusiveGroup(options) -> ArgumentGroup
* - options (Object): {required: false}
*
* Create new mutual exclusive groups
**/
ActionContainer.prototype.addMutuallyExclusiveGroup = function (options) {
var group = new MutuallyExclusiveGroup(this, options);
this._mutuallyExclusiveGroups.push(group);
return group;
};
ActionContainer.prototype._addAction = function (action) {
var self = this;
// resolve any conflicts
this._checkConflict(action);
// add to actions list
this._actions.push(action);
action.container = this;
// index the action by any option strings it has
action.optionStrings.forEach(function (optionString) {
self._optionStringActions[optionString] = action;
});
// set the flag if any option strings look like negative numbers
action.optionStrings.forEach(function (optionString) {
if (optionString.match(self._regexpNegativeNumber)) {
if (!self._hasNegativeNumberOptionals.some(Boolean)) {
self._hasNegativeNumberOptionals.push(true);
}
}
});
// return the created action
return action;
};
ActionContainer.prototype._removeAction = function (action) {
var actionIndex = this._actions.indexOf(action);
if (actionIndex >= 0) {
this._actions.splice(actionIndex, 1);
}
};
ActionContainer.prototype._addContainerActions = function (container) {
// collect groups by titles
var titleGroupMap = {};
this._actionGroups.forEach(function (group) {
if (titleGroupMap[group.title]) {
throw new Error(format('Cannot merge actions - two groups are named "%s".', group.title));
}
titleGroupMap[group.title] = group;
});
// map each action to its group
var groupMap = {};
function actionHash(action) {
// unique (hopefully?) string suitable as dictionary key
return action.getName();
}
container._actionGroups.forEach(function (group) {
// if a group with the title exists, use that, otherwise
// create a new group matching the container's group
if (!titleGroupMap[group.title]) {
titleGroupMap[group.title] = this.addArgumentGroup({
title: group.title,
description: group.description
});
}
// map the actions to their new group
group._groupActions.forEach(function (action) {
groupMap[actionHash(action)] = titleGroupMap[group.title];
});
}, this);
// add container's mutually exclusive groups
// NOTE: if add_mutually_exclusive_group ever gains title= and
// description= then this code will need to be expanded as above
var mutexGroup;
container._mutuallyExclusiveGroups.forEach(function (group) {
mutexGroup = this.addMutuallyExclusiveGroup({
required: group.required
});
// map the actions to their new mutex group
group._groupActions.forEach(function (action) {
groupMap[actionHash(action)] = mutexGroup;
});
}, this); // forEach takes a 'this' argument
// add all actions to this container or their group
container._actions.forEach(function (action) {
var key = actionHash(action);
if (groupMap[key]) {
groupMap[key]._addAction(action);
} else {
this._addAction(action);
}
});
};
ActionContainer.prototype._getPositional = function (dest, options) {
if (Array.isArray(dest)) {
dest = dest[0];
}
// make sure required is not specified
if (options.required) {
throw new Error('"required" is an invalid argument for positionals.');
}
// mark positional arguments as required if at least one is
// always required
if (options.nargs !== c.OPTIONAL && options.nargs !== c.ZERO_OR_MORE) {
options.required = true;
}
if (options.nargs === c.ZERO_OR_MORE && typeof options.defaultValue === 'undefined') {
options.required = true;
}
// return the keyword arguments with no option strings
options.dest = dest;
options.optionStrings = [];
return options;
};
ActionContainer.prototype._getOptional = function (args, options) {
var prefixChars = this.prefixChars;
var optionStrings = [];
var optionStringsLong = [];
// determine short and long option strings
args.forEach(function (optionString) {
// error on strings that don't start with an appropriate prefix
if (prefixChars.indexOf(optionString[0]) < 0) {
throw new Error(format('Invalid option string "%s": must start with a "%s".',
optionString,
prefixChars
));
}
// strings starting with two prefix characters are long options
optionStrings.push(optionString);
if (optionString.length > 1 && prefixChars.indexOf(optionString[1]) >= 0) {
optionStringsLong.push(optionString);
}
});
// infer dest, '--foo-bar' -> 'foo_bar' and '-x' -> 'x'
var dest = options.dest || null;
delete options.dest;
if (!dest) {
var optionStringDest = optionStringsLong.length ? optionStringsLong[0] : optionStrings[0];
dest = $$.trimChars(optionStringDest, this.prefixChars);
if (dest.length === 0) {
throw new Error(
format('dest= is required for options like "%s"', optionStrings.join(', '))
);
}
dest = dest.replace(/-/g, '_');
}
// return the updated keyword arguments
options.dest = dest;
options.optionStrings = optionStrings;
return options;
};
ActionContainer.prototype._popActionClass = function (options, defaultValue) {
defaultValue = defaultValue || null;
var action = (options.action || defaultValue);
delete options.action;
var actionClass = this._registryGet('action', action, action);
return actionClass;
};
ActionContainer.prototype._getHandler = function () {
var handlerString = this.conflictHandler;
var handlerFuncName = '_handleConflict' + $$.capitalize(handlerString);
var func = this[handlerFuncName];
if (typeof func === 'undefined') {
var msg = 'invalid conflict resolution value: ' + handlerString;
throw new Error(msg);
} else {
return func;
}
};
ActionContainer.prototype._checkConflict = function (action) {
var optionStringActions = this._optionStringActions;
var conflictOptionals = [];
// find all options that conflict with this option
// collect pairs, the string, and an existing action that it conflicts with
action.optionStrings.forEach(function (optionString) {
var conflOptional = optionStringActions[optionString];
if (typeof conflOptional !== 'undefined') {
conflictOptionals.push([ optionString, conflOptional ]);
}
});
if (conflictOptionals.length > 0) {
var conflictHandler = this._getHandler();
conflictHandler.call(this, action, conflictOptionals);
}
};
ActionContainer.prototype._handleConflictError = function (action, conflOptionals) {
var conflicts = conflOptionals.map(function (pair) { return pair[0]; });
conflicts = conflicts.join(', ');
throw argumentErrorHelper(
action,
format('Conflicting option string(s): %s', conflicts)
);
};
ActionContainer.prototype._handleConflictResolve = function (action, conflOptionals) {
// remove all conflicting options
var self = this;
conflOptionals.forEach(function (pair) {
var optionString = pair[0];
var conflictingAction = pair[1];
// remove the conflicting option string
var i = conflictingAction.optionStrings.indexOf(optionString);
if (i >= 0) {
conflictingAction.optionStrings.splice(i, 1);
}
delete self._optionStringActions[optionString];
// if the option now has no option string, remove it from the
// container holding it
if (conflictingAction.optionStrings.length === 0) {
conflictingAction.container._removeAction(conflictingAction);
}
});
};
+14
View File
@@ -0,0 +1,14 @@
'use strict';
module.exports.ArgumentParser = require('./argument_parser.js');
module.exports.Namespace = require('./namespace');
module.exports.Action = require('./action');
module.exports.HelpFormatter = require('./help/formatter.js');
module.exports.Const = require('./const.js');
module.exports.ArgumentDefaultsHelpFormatter =
require('./help/added_formatters.js').ArgumentDefaultsHelpFormatter;
module.exports.RawDescriptionHelpFormatter =
require('./help/added_formatters.js').RawDescriptionHelpFormatter;
module.exports.RawTextHelpFormatter =
require('./help/added_formatters.js').RawTextHelpFormatter;
+50
View File
@@ -0,0 +1,50 @@
'use strict';
var format = require('util').format;
var ERR_CODE = 'ARGError';
/*:nodoc:*
* argumentError(argument, message) -> TypeError
* - argument (Object): action with broken argument
* - message (String): error message
*
* Error format helper. An error from creating or using an argument
* (optional or positional). The string value of this exception
* is the message, augmented with information
* about the argument that caused it.
*
* #####Example
*
* var argumentErrorHelper = require('./argument/error');
* if (conflictOptionals.length > 0) {
* throw argumentErrorHelper(
* action,
* format('Conflicting option string(s): %s', conflictOptionals.join(', '))
* );
* }
*
**/
module.exports = function (argument, message) {
var argumentName = null;
var errMessage;
var err;
if (argument.getName) {
argumentName = argument.getName();
} else {
argumentName = '' + argument;
}
if (!argumentName) {
errMessage = message;
} else {
errMessage = format('argument "%s": %s', argumentName, message);
}
err = new TypeError(errMessage);
err.code = ERR_CODE;
return err;
};
+54
View File
@@ -0,0 +1,54 @@
/** internal
* class MutuallyExclusiveGroup
*
* Group arguments.
* By default, ArgumentParser groups command-line arguments
* into “positional arguments” and “optional arguments”
* when displaying help messages. When there is a better
* conceptual grouping of arguments than this default one,
* appropriate groups can be created using the addArgumentGroup() method
*
* This class inherited from [[ArgumentContainer]]
**/
'use strict';
var util = require('util');
var ArgumentGroup = require('./group');
/**
* new MutuallyExclusiveGroup(container, options)
* - container (object): main container
* - options (object): options.required -> true/false
*
* `required` could be an argument itself, but making it a property of
* the options argument is more consistent with the JS adaptation of the Python)
**/
var MutuallyExclusiveGroup = module.exports = function MutuallyExclusiveGroup(container, options) {
var required;
options = options || {};
required = options.required || false;
ArgumentGroup.call(this, container);
this.required = required;
};
util.inherits(MutuallyExclusiveGroup, ArgumentGroup);
MutuallyExclusiveGroup.prototype._addAction = function (action) {
var msg;
if (action.required) {
msg = 'mutually exclusive arguments must be optional';
throw new Error(msg);
}
action = this._container._addAction(action);
this._groupActions.push(action);
return action;
};
MutuallyExclusiveGroup.prototype._removeAction = function (action) {
this._container._removeAction(action);
this._groupActions.remove(action);
};
+75
View File
@@ -0,0 +1,75 @@
/** internal
* class ArgumentGroup
*
* Group arguments.
* By default, ArgumentParser groups command-line arguments
* into “positional arguments” and “optional arguments”
* when displaying help messages. When there is a better
* conceptual grouping of arguments than this default one,
* appropriate groups can be created using the addArgumentGroup() method
*
* This class inherited from [[ArgumentContainer]]
**/
'use strict';
var util = require('util');
var ActionContainer = require('../action_container');
/**
* new ArgumentGroup(container, options)
* - container (object): main container
* - options (object): hash of group options
*
* #### options
* - **prefixChars** group name prefix
* - **argumentDefault** default argument value
* - **title** group title
* - **description** group description
*
**/
var ArgumentGroup = module.exports = function ArgumentGroup(container, options) {
options = options || {};
// add any missing keyword arguments by checking the container
options.conflictHandler = (options.conflictHandler || container.conflictHandler);
options.prefixChars = (options.prefixChars || container.prefixChars);
options.argumentDefault = (options.argumentDefault || container.argumentDefault);
ActionContainer.call(this, options);
// group attributes
this.title = options.title;
this._groupActions = [];
// share most attributes with the container
this._container = container;
this._registries = container._registries;
this._actions = container._actions;
this._optionStringActions = container._optionStringActions;
this._defaults = container._defaults;
this._hasNegativeNumberOptionals = container._hasNegativeNumberOptionals;
this._mutuallyExclusiveGroups = container._mutuallyExclusiveGroups;
};
util.inherits(ArgumentGroup, ActionContainer);
ArgumentGroup.prototype._addAction = function (action) {
// Parent add action
action = ActionContainer.prototype._addAction.call(this, action);
this._groupActions.push(action);
return action;
};
ArgumentGroup.prototype._removeAction = function (action) {
// Parent remove action
ActionContainer.prototype._removeAction.call(this, action);
var actionIndex = this._groupActions.indexOf(action);
if (actionIndex >= 0) {
this._groupActions.splice(actionIndex, 1);
}
};
+1161
View File
File diff suppressed because it is too large Load Diff
+21
View File
@@ -0,0 +1,21 @@
//
// Constants
//
'use strict';
module.exports.EOL = '\n';
module.exports.SUPPRESS = '==SUPPRESS==';
module.exports.OPTIONAL = '?';
module.exports.ZERO_OR_MORE = '*';
module.exports.ONE_OR_MORE = '+';
module.exports.PARSER = 'A...';
module.exports.REMAINDER = '...';
module.exports._UNRECOGNIZED_ARGS_ATTR = '_unrecognized_args';
+87
View File
@@ -0,0 +1,87 @@
'use strict';
var util = require('util');
// Constants
var c = require('../const');
var $$ = require('../utils');
var HelpFormatter = require('./formatter.js');
/**
* new RawDescriptionHelpFormatter(options)
* new ArgumentParser({formatterClass: argparse.RawDescriptionHelpFormatter, ...})
*
* Help message formatter which adds default values to argument help.
*
* Only the name of this class is considered a public API. All the methods
* provided by the class are considered an implementation detail.
**/
function ArgumentDefaultsHelpFormatter(options) {
HelpFormatter.call(this, options);
}
util.inherits(ArgumentDefaultsHelpFormatter, HelpFormatter);
ArgumentDefaultsHelpFormatter.prototype._getHelpString = function (action) {
var help = action.help;
if (action.help.indexOf('%(defaultValue)s') === -1) {
if (action.defaultValue !== c.SUPPRESS) {
var defaulting_nargs = [ c.OPTIONAL, c.ZERO_OR_MORE ];
if (action.isOptional() || (defaulting_nargs.indexOf(action.nargs) >= 0)) {
help += ' (default: %(defaultValue)s)';
}
}
}
return help;
};
module.exports.ArgumentDefaultsHelpFormatter = ArgumentDefaultsHelpFormatter;
/**
* new RawDescriptionHelpFormatter(options)
* new ArgumentParser({formatterClass: argparse.RawDescriptionHelpFormatter, ...})
*
* Help message formatter which retains any formatting in descriptions.
*
* Only the name of this class is considered a public API. All the methods
* provided by the class are considered an implementation detail.
**/
function RawDescriptionHelpFormatter(options) {
HelpFormatter.call(this, options);
}
util.inherits(RawDescriptionHelpFormatter, HelpFormatter);
RawDescriptionHelpFormatter.prototype._fillText = function (text, width, indent) {
var lines = text.split('\n');
lines = lines.map(function (line) {
return $$.trimEnd(indent + line);
});
return lines.join('\n');
};
module.exports.RawDescriptionHelpFormatter = RawDescriptionHelpFormatter;
/**
* new RawTextHelpFormatter(options)
* new ArgumentParser({formatterClass: argparse.RawTextHelpFormatter, ...})
*
* Help message formatter which retains formatting of all help text.
*
* Only the name of this class is considered a public API. All the methods
* provided by the class are considered an implementation detail.
**/
function RawTextHelpFormatter(options) {
RawDescriptionHelpFormatter.call(this, options);
}
util.inherits(RawTextHelpFormatter, RawDescriptionHelpFormatter);
RawTextHelpFormatter.prototype._splitLines = function (text) {
return text.split('\n');
};
module.exports.RawTextHelpFormatter = RawTextHelpFormatter;
+795
View File
@@ -0,0 +1,795 @@
/**
* class HelpFormatter
*
* Formatter for generating usage messages and argument help strings. Only the
* name of this class is considered a public API. All the methods provided by
* the class are considered an implementation detail.
*
* Do not call in your code, use this class only for inherits your own forvatter
*
* ToDo add [additonal formatters][1]
*
* [1]:http://docs.python.org/dev/library/argparse.html#formatter-class
**/
'use strict';
var sprintf = require('sprintf-js').sprintf;
// Constants
var c = require('../const');
var $$ = require('../utils');
/*:nodoc:* internal
* new Support(parent, heding)
* - parent (object): parent section
* - heading (string): header string
*
**/
function Section(parent, heading) {
this._parent = parent;
this._heading = heading;
this._items = [];
}
/*:nodoc:* internal
* Section#addItem(callback) -> Void
* - callback (array): tuple with function and args
*
* Add function for single element
**/
Section.prototype.addItem = function (callback) {
this._items.push(callback);
};
/*:nodoc:* internal
* Section#formatHelp(formatter) -> string
* - formatter (HelpFormatter): current formatter
*
* Form help section string
*
**/
Section.prototype.formatHelp = function (formatter) {
var itemHelp, heading;
// format the indented section
if (this._parent) {
formatter._indent();
}
itemHelp = this._items.map(function (item) {
var obj, func, args;
obj = formatter;
func = item[0];
args = item[1];
return func.apply(obj, args);
});
itemHelp = formatter._joinParts(itemHelp);
if (this._parent) {
formatter._dedent();
}
// return nothing if the section was empty
if (!itemHelp) {
return '';
}
// add the heading if the section was non-empty
heading = '';
if (this._heading && this._heading !== c.SUPPRESS) {
var currentIndent = formatter.currentIndent;
heading = $$.repeat(' ', currentIndent) + this._heading + ':' + c.EOL;
}
// join the section-initialize newline, the heading and the help
return formatter._joinParts([ c.EOL, heading, itemHelp, c.EOL ]);
};
/**
* new HelpFormatter(options)
*
* #### Options:
* - `prog`: program name
* - `indentIncriment`: indent step, default value 2
* - `maxHelpPosition`: max help position, default value = 24
* - `width`: line width
*
**/
var HelpFormatter = module.exports = function HelpFormatter(options) {
options = options || {};
this._prog = options.prog;
this._maxHelpPosition = options.maxHelpPosition || 24;
this._width = (options.width || ((process.env.COLUMNS || 80) - 2));
this._currentIndent = 0;
this._indentIncriment = options.indentIncriment || 2;
this._level = 0;
this._actionMaxLength = 0;
this._rootSection = new Section(null);
this._currentSection = this._rootSection;
this._whitespaceMatcher = new RegExp('\\s+', 'g');
this._longBreakMatcher = new RegExp(c.EOL + c.EOL + c.EOL + '+', 'g');
};
HelpFormatter.prototype._indent = function () {
this._currentIndent += this._indentIncriment;
this._level += 1;
};
HelpFormatter.prototype._dedent = function () {
this._currentIndent -= this._indentIncriment;
this._level -= 1;
if (this._currentIndent < 0) {
throw new Error('Indent decreased below 0.');
}
};
HelpFormatter.prototype._addItem = function (func, args) {
this._currentSection.addItem([ func, args ]);
};
//
// Message building methods
//
/**
* HelpFormatter#startSection(heading) -> Void
* - heading (string): header string
*
* Start new help section
*
* See alse [code example][1]
*
* ##### Example
*
* formatter.startSection(actionGroup.title);
* formatter.addText(actionGroup.description);
* formatter.addArguments(actionGroup._groupActions);
* formatter.endSection();
*
**/
HelpFormatter.prototype.startSection = function (heading) {
this._indent();
var section = new Section(this._currentSection, heading);
var func = section.formatHelp.bind(section);
this._addItem(func, [ this ]);
this._currentSection = section;
};
/**
* HelpFormatter#endSection -> Void
*
* End help section
*
* ##### Example
*
* formatter.startSection(actionGroup.title);
* formatter.addText(actionGroup.description);
* formatter.addArguments(actionGroup._groupActions);
* formatter.endSection();
**/
HelpFormatter.prototype.endSection = function () {
this._currentSection = this._currentSection._parent;
this._dedent();
};
/**
* HelpFormatter#addText(text) -> Void
* - text (string): plain text
*
* Add plain text into current section
*
* ##### Example
*
* formatter.startSection(actionGroup.title);
* formatter.addText(actionGroup.description);
* formatter.addArguments(actionGroup._groupActions);
* formatter.endSection();
*
**/
HelpFormatter.prototype.addText = function (text) {
if (text && text !== c.SUPPRESS) {
this._addItem(this._formatText, [ text ]);
}
};
/**
* HelpFormatter#addUsage(usage, actions, groups, prefix) -> Void
* - usage (string): usage text
* - actions (array): actions list
* - groups (array): groups list
* - prefix (string): usage prefix
*
* Add usage data into current section
*
* ##### Example
*
* formatter.addUsage(this.usage, this._actions, []);
* return formatter.formatHelp();
*
**/
HelpFormatter.prototype.addUsage = function (usage, actions, groups, prefix) {
if (usage !== c.SUPPRESS) {
this._addItem(this._formatUsage, [ usage, actions, groups, prefix ]);
}
};
/**
* HelpFormatter#addArgument(action) -> Void
* - action (object): action
*
* Add argument into current section
*
* Single variant of [[HelpFormatter#addArguments]]
**/
HelpFormatter.prototype.addArgument = function (action) {
if (action.help !== c.SUPPRESS) {
var self = this;
// find all invocations
var invocations = [ this._formatActionInvocation(action) ];
var invocationLength = invocations[0].length;
var actionLength;
if (action._getSubactions) {
this._indent();
action._getSubactions().forEach(function (subaction) {
var invocationNew = self._formatActionInvocation(subaction);
invocations.push(invocationNew);
invocationLength = Math.max(invocationLength, invocationNew.length);
});
this._dedent();
}
// update the maximum item length
actionLength = invocationLength + this._currentIndent;
this._actionMaxLength = Math.max(this._actionMaxLength, actionLength);
// add the item to the list
this._addItem(this._formatAction, [ action ]);
}
};
/**
* HelpFormatter#addArguments(actions) -> Void
* - actions (array): actions list
*
* Mass add arguments into current section
*
* ##### Example
*
* formatter.startSection(actionGroup.title);
* formatter.addText(actionGroup.description);
* formatter.addArguments(actionGroup._groupActions);
* formatter.endSection();
*
**/
HelpFormatter.prototype.addArguments = function (actions) {
var self = this;
actions.forEach(function (action) {
self.addArgument(action);
});
};
//
// Help-formatting methods
//
/**
* HelpFormatter#formatHelp -> string
*
* Format help
*
* ##### Example
*
* formatter.addText(this.epilog);
* return formatter.formatHelp();
*
**/
HelpFormatter.prototype.formatHelp = function () {
var help = this._rootSection.formatHelp(this);
if (help) {
help = help.replace(this._longBreakMatcher, c.EOL + c.EOL);
help = $$.trimChars(help, c.EOL) + c.EOL;
}
return help;
};
HelpFormatter.prototype._joinParts = function (partStrings) {
return partStrings.filter(function (part) {
return (part && part !== c.SUPPRESS);
}).join('');
};
HelpFormatter.prototype._formatUsage = function (usage, actions, groups, prefix) {
if (!prefix && typeof prefix !== 'string') {
prefix = 'usage: ';
}
actions = actions || [];
groups = groups || [];
// if usage is specified, use that
if (usage) {
usage = sprintf(usage, { prog: this._prog });
// if no optionals or positionals are available, usage is just prog
} else if (!usage && actions.length === 0) {
usage = this._prog;
// if optionals and positionals are available, calculate usage
} else if (!usage) {
var prog = this._prog;
var optionals = [];
var positionals = [];
var actionUsage;
var textWidth;
// split optionals from positionals
actions.forEach(function (action) {
if (action.isOptional()) {
optionals.push(action);
} else {
positionals.push(action);
}
});
// build full usage string
actionUsage = this._formatActionsUsage([].concat(optionals, positionals), groups);
usage = [ prog, actionUsage ].join(' ');
// wrap the usage parts if it's too long
textWidth = this._width - this._currentIndent;
if ((prefix.length + usage.length) > textWidth) {
// break usage into wrappable parts
var regexpPart = new RegExp('\\(.*?\\)+|\\[.*?\\]+|\\S+', 'g');
var optionalUsage = this._formatActionsUsage(optionals, groups);
var positionalUsage = this._formatActionsUsage(positionals, groups);
var optionalParts = optionalUsage.match(regexpPart);
var positionalParts = positionalUsage.match(regexpPart) || [];
if (optionalParts.join(' ') !== optionalUsage) {
throw new Error('assert "optionalParts.join(\' \') === optionalUsage"');
}
if (positionalParts.join(' ') !== positionalUsage) {
throw new Error('assert "positionalParts.join(\' \') === positionalUsage"');
}
// helper for wrapping lines
/*eslint-disable func-style*/ // node 0.10 compat
var _getLines = function (parts, indent, prefix) {
var lines = [];
var line = [];
var lineLength = prefix ? prefix.length - 1 : indent.length - 1;
parts.forEach(function (part) {
if (lineLength + 1 + part.length > textWidth) {
lines.push(indent + line.join(' '));
line = [];
lineLength = indent.length - 1;
}
line.push(part);
lineLength += part.length + 1;
});
if (line) {
lines.push(indent + line.join(' '));
}
if (prefix) {
lines[0] = lines[0].substr(indent.length);
}
return lines;
};
var lines, indent, parts;
// if prog is short, follow it with optionals or positionals
if (prefix.length + prog.length <= 0.75 * textWidth) {
indent = $$.repeat(' ', (prefix.length + prog.length + 1));
if (optionalParts) {
lines = [].concat(
_getLines([ prog ].concat(optionalParts), indent, prefix),
_getLines(positionalParts, indent)
);
} else if (positionalParts) {
lines = _getLines([ prog ].concat(positionalParts), indent, prefix);
} else {
lines = [ prog ];
}
// if prog is long, put it on its own line
} else {
indent = $$.repeat(' ', prefix.length);
parts = optionalParts.concat(positionalParts);
lines = _getLines(parts, indent);
if (lines.length > 1) {
lines = [].concat(
_getLines(optionalParts, indent),
_getLines(positionalParts, indent)
);
}
lines = [ prog ].concat(lines);
}
// join lines into usage
usage = lines.join(c.EOL);
}
}
// prefix with 'usage:'
return prefix + usage + c.EOL + c.EOL;
};
HelpFormatter.prototype._formatActionsUsage = function (actions, groups) {
// find group indices and identify actions in groups
var groupActions = [];
var inserts = [];
var self = this;
groups.forEach(function (group) {
var end;
var i;
var start = actions.indexOf(group._groupActions[0]);
if (start >= 0) {
end = start + group._groupActions.length;
//if (actions.slice(start, end) === group._groupActions) {
if ($$.arrayEqual(actions.slice(start, end), group._groupActions)) {
group._groupActions.forEach(function (action) {
groupActions.push(action);
});
if (!group.required) {
if (inserts[start]) {
inserts[start] += ' [';
} else {
inserts[start] = '[';
}
inserts[end] = ']';
} else {
if (inserts[start]) {
inserts[start] += ' (';
} else {
inserts[start] = '(';
}
inserts[end] = ')';
}
for (i = start + 1; i < end; i += 1) {
inserts[i] = '|';
}
}
}
});
// collect all actions format strings
var parts = [];
actions.forEach(function (action, actionIndex) {
var part;
var optionString;
var argsDefault;
var argsString;
// suppressed arguments are marked with None
// remove | separators for suppressed arguments
if (action.help === c.SUPPRESS) {
parts.push(null);
if (inserts[actionIndex] === '|') {
inserts.splice(actionIndex, actionIndex);
} else if (inserts[actionIndex + 1] === '|') {
inserts.splice(actionIndex + 1, actionIndex + 1);
}
// produce all arg strings
} else if (!action.isOptional()) {
part = self._formatArgs(action, action.dest);
// if it's in a group, strip the outer []
if (groupActions.indexOf(action) >= 0) {
if (part[0] === '[' && part[part.length - 1] === ']') {
part = part.slice(1, -1);
}
}
// add the action string to the list
parts.push(part);
// produce the first way to invoke the option in brackets
} else {
optionString = action.optionStrings[0];
// if the Optional doesn't take a value, format is: -s or --long
if (action.nargs === 0) {
part = '' + optionString;
// if the Optional takes a value, format is: -s ARGS or --long ARGS
} else {
argsDefault = action.dest.toUpperCase();
argsString = self._formatArgs(action, argsDefault);
part = optionString + ' ' + argsString;
}
// make it look optional if it's not required or in a group
if (!action.required && groupActions.indexOf(action) < 0) {
part = '[' + part + ']';
}
// add the action string to the list
parts.push(part);
}
});
// insert things at the necessary indices
for (var i = inserts.length - 1; i >= 0; --i) {
if (inserts[i] !== null) {
parts.splice(i, 0, inserts[i]);
}
}
// join all the action items with spaces
var text = parts.filter(function (part) {
return !!part;
}).join(' ');
// clean up separators for mutually exclusive groups
text = text.replace(/([\[(]) /g, '$1'); // remove spaces
text = text.replace(/ ([\])])/g, '$1');
text = text.replace(/\[ *\]/g, ''); // remove empty groups
text = text.replace(/\( *\)/g, '');
text = text.replace(/\(([^|]*)\)/g, '$1'); // remove () from single action groups
text = text.trim();
// return the text
return text;
};
HelpFormatter.prototype._formatText = function (text) {
text = sprintf(text, { prog: this._prog });
var textWidth = this._width - this._currentIndent;
var indentIncriment = $$.repeat(' ', this._currentIndent);
return this._fillText(text, textWidth, indentIncriment) + c.EOL + c.EOL;
};
HelpFormatter.prototype._formatAction = function (action) {
var self = this;
var helpText;
var helpLines;
var parts;
var indentFirst;
// determine the required width and the entry label
var helpPosition = Math.min(this._actionMaxLength + 2, this._maxHelpPosition);
var helpWidth = this._width - helpPosition;
var actionWidth = helpPosition - this._currentIndent - 2;
var actionHeader = this._formatActionInvocation(action);
// no help; start on same line and add a final newline
if (!action.help) {
actionHeader = $$.repeat(' ', this._currentIndent) + actionHeader + c.EOL;
// short action name; start on the same line and pad two spaces
} else if (actionHeader.length <= actionWidth) {
actionHeader = $$.repeat(' ', this._currentIndent) +
actionHeader +
' ' +
$$.repeat(' ', actionWidth - actionHeader.length);
indentFirst = 0;
// long action name; start on the next line
} else {
actionHeader = $$.repeat(' ', this._currentIndent) + actionHeader + c.EOL;
indentFirst = helpPosition;
}
// collect the pieces of the action help
parts = [ actionHeader ];
// if there was help for the action, add lines of help text
if (action.help) {
helpText = this._expandHelp(action);
helpLines = this._splitLines(helpText, helpWidth);
parts.push($$.repeat(' ', indentFirst) + helpLines[0] + c.EOL);
helpLines.slice(1).forEach(function (line) {
parts.push($$.repeat(' ', helpPosition) + line + c.EOL);
});
// or add a newline if the description doesn't end with one
} else if (actionHeader.charAt(actionHeader.length - 1) !== c.EOL) {
parts.push(c.EOL);
}
// if there are any sub-actions, add their help as well
if (action._getSubactions) {
this._indent();
action._getSubactions().forEach(function (subaction) {
parts.push(self._formatAction(subaction));
});
this._dedent();
}
// return a single string
return this._joinParts(parts);
};
HelpFormatter.prototype._formatActionInvocation = function (action) {
if (!action.isOptional()) {
var format_func = this._metavarFormatter(action, action.dest);
var metavars = format_func(1);
return metavars[0];
}
var parts = [];
var argsDefault;
var argsString;
// if the Optional doesn't take a value, format is: -s, --long
if (action.nargs === 0) {
parts = parts.concat(action.optionStrings);
// if the Optional takes a value, format is: -s ARGS, --long ARGS
} else {
argsDefault = action.dest.toUpperCase();
argsString = this._formatArgs(action, argsDefault);
action.optionStrings.forEach(function (optionString) {
parts.push(optionString + ' ' + argsString);
});
}
return parts.join(', ');
};
HelpFormatter.prototype._metavarFormatter = function (action, metavarDefault) {
var result;
if (action.metavar || action.metavar === '') {
result = action.metavar;
} else if (action.choices) {
var choices = action.choices;
if (typeof choices === 'string') {
choices = choices.split('').join(', ');
} else if (Array.isArray(choices)) {
choices = choices.join(',');
} else {
choices = Object.keys(choices).join(',');
}
result = '{' + choices + '}';
} else {
result = metavarDefault;
}
return function (size) {
if (Array.isArray(result)) {
return result;
}
var metavars = [];
for (var i = 0; i < size; i += 1) {
metavars.push(result);
}
return metavars;
};
};
HelpFormatter.prototype._formatArgs = function (action, metavarDefault) {
var result;
var metavars;
var buildMetavar = this._metavarFormatter(action, metavarDefault);
switch (action.nargs) {
/*eslint-disable no-undefined*/
case undefined:
case null:
metavars = buildMetavar(1);
result = '' + metavars[0];
break;
case c.OPTIONAL:
metavars = buildMetavar(1);
result = '[' + metavars[0] + ']';
break;
case c.ZERO_OR_MORE:
metavars = buildMetavar(2);
result = '[' + metavars[0] + ' [' + metavars[1] + ' ...]]';
break;
case c.ONE_OR_MORE:
metavars = buildMetavar(2);
result = '' + metavars[0] + ' [' + metavars[1] + ' ...]';
break;
case c.REMAINDER:
result = '...';
break;
case c.PARSER:
metavars = buildMetavar(1);
result = metavars[0] + ' ...';
break;
default:
metavars = buildMetavar(action.nargs);
result = metavars.join(' ');
}
return result;
};
HelpFormatter.prototype._expandHelp = function (action) {
var params = { prog: this._prog };
Object.keys(action).forEach(function (actionProperty) {
var actionValue = action[actionProperty];
if (actionValue !== c.SUPPRESS) {
params[actionProperty] = actionValue;
}
});
if (params.choices) {
if (typeof params.choices === 'string') {
params.choices = params.choices.split('').join(', ');
} else if (Array.isArray(params.choices)) {
params.choices = params.choices.join(', ');
} else {
params.choices = Object.keys(params.choices).join(', ');
}
}
return sprintf(this._getHelpString(action), params);
};
HelpFormatter.prototype._splitLines = function (text, width) {
var lines = [];
var delimiters = [ ' ', '.', ',', '!', '?' ];
var re = new RegExp('[' + delimiters.join('') + '][^' + delimiters.join('') + ']*$');
text = text.replace(/[\n\|\t]/g, ' ');
text = text.trim();
text = text.replace(this._whitespaceMatcher, ' ');
// Wraps the single paragraph in text (a string) so every line
// is at most width characters long.
text.split(c.EOL).forEach(function (line) {
if (width >= line.length) {
lines.push(line);
return;
}
var wrapStart = 0;
var wrapEnd = width;
var delimiterIndex = 0;
while (wrapEnd <= line.length) {
if (wrapEnd !== line.length && delimiters.indexOf(line[wrapEnd] < -1)) {
delimiterIndex = (re.exec(line.substring(wrapStart, wrapEnd)) || {}).index;
wrapEnd = wrapStart + delimiterIndex + 1;
}
lines.push(line.substring(wrapStart, wrapEnd));
wrapStart = wrapEnd;
wrapEnd += width;
}
if (wrapStart < line.length) {
lines.push(line.substring(wrapStart, wrapEnd));
}
});
return lines;
};
HelpFormatter.prototype._fillText = function (text, width, indent) {
var lines = this._splitLines(text, width);
lines = lines.map(function (line) {
return indent + line;
});
return lines.join(c.EOL);
};
HelpFormatter.prototype._getHelpString = function (action) {
return action.help;
};
+76
View File
@@ -0,0 +1,76 @@
/**
* class Namespace
*
* Simple object for storing attributes. Implements equality by attribute names
* and values, and provides a simple string representation.
*
* See also [original guide][1]
*
* [1]:http://docs.python.org/dev/library/argparse.html#the-namespace-object
**/
'use strict';
var $$ = require('./utils');
/**
* new Namespace(options)
* - options(object): predefined propertis for result object
*
**/
var Namespace = module.exports = function Namespace(options) {
$$.extend(this, options);
};
/**
* Namespace#isset(key) -> Boolean
* - key (string|number): property name
*
* Tells whenever `namespace` contains given `key` or not.
**/
Namespace.prototype.isset = function (key) {
return $$.has(this, key);
};
/**
* Namespace#set(key, value) -> self
* -key (string|number|object): propery name
* -value (mixed): new property value
*
* Set the property named key with value.
* If key object then set all key properties to namespace object
**/
Namespace.prototype.set = function (key, value) {
if (typeof (key) === 'object') {
$$.extend(this, key);
} else {
this[key] = value;
}
return this;
};
/**
* Namespace#get(key, defaultValue) -> mixed
* - key (string|number): property name
* - defaultValue (mixed): default value
*
* Return the property key or defaulValue if not set
**/
Namespace.prototype.get = function (key, defaultValue) {
return !this[key] ? defaultValue : this[key];
};
/**
* Namespace#unset(key, defaultValue) -> mixed
* - key (string|number): property name
* - defaultValue (mixed): default value
*
* Return data[key](and delete it) or defaultValue
**/
Namespace.prototype.unset = function (key, defaultValue) {
var value = this[key];
if (value !== null) {
delete this[key];
return value;
}
return defaultValue;
};
+57
View File
@@ -0,0 +1,57 @@
'use strict';
exports.repeat = function (str, num) {
var result = '';
for (var i = 0; i < num; i++) { result += str; }
return result;
};
exports.arrayEqual = function (a, b) {
if (a.length !== b.length) { return false; }
for (var i = 0; i < a.length; i++) {
if (a[i] !== b[i]) { return false; }
}
return true;
};
exports.trimChars = function (str, chars) {
var start = 0;
var end = str.length - 1;
while (chars.indexOf(str.charAt(start)) >= 0) { start++; }
while (chars.indexOf(str.charAt(end)) >= 0) { end--; }
return str.slice(start, end + 1);
};
exports.capitalize = function (str) {
return str.charAt(0).toUpperCase() + str.slice(1);
};
exports.arrayUnion = function () {
var result = [];
for (var i = 0, values = {}; i < arguments.length; i++) {
var arr = arguments[i];
for (var j = 0; j < arr.length; j++) {
if (!values[arr[j]]) {
values[arr[j]] = true;
result.push(arr[j]);
}
}
}
return result;
};
function has(obj, key) {
return Object.prototype.hasOwnProperty.call(obj, key);
}
exports.has = has;
exports.extend = function (dest, src) {
for (var i in src) {
if (has(src, i)) { dest[i] = src[i]; }
}
};
exports.trimEnd = function (str) {
return str.replace(/\s+$/g, '');
};
+70
View File
@@ -0,0 +1,70 @@
{
"_from": "argparse@^1.0.7",
"_id": "argparse@1.0.10",
"_inBundle": false,
"_integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
"_location": "/argparse",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "argparse@^1.0.7",
"name": "argparse",
"escapedName": "argparse",
"rawSpec": "^1.0.7",
"saveSpec": null,
"fetchSpec": "^1.0.7"
},
"_requiredBy": [
"/js-yaml"
],
"_resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
"_shasum": "bcd6791ea5ae09725e17e5ad988134cd40b3d911",
"_spec": "argparse@^1.0.7",
"_where": "/Users/aj/repos/k8s-deploy/node_modules/js-yaml",
"bugs": {
"url": "https://github.com/nodeca/argparse/issues"
},
"bundleDependencies": false,
"contributors": [
{
"name": "Eugene Shkuropat"
},
{
"name": "Paul Jacobson"
}
],
"dependencies": {
"sprintf-js": "~1.0.2"
},
"deprecated": false,
"description": "Very powerful CLI arguments parser. Native port of argparse - python's options parsing library",
"devDependencies": {
"eslint": "^2.13.1",
"istanbul": "^0.4.5",
"mocha": "^3.1.0",
"ndoc": "^5.0.1"
},
"files": [
"index.js",
"lib/"
],
"homepage": "https://github.com/nodeca/argparse#readme",
"keywords": [
"cli",
"parser",
"argparse",
"option",
"args"
],
"license": "MIT",
"name": "argparse",
"repository": {
"type": "git",
"url": "git+https://github.com/nodeca/argparse.git"
},
"scripts": {
"test": "make test"
},
"version": "1.0.10"
}

Some files were not shown because too many files have changed in this diff Show More