Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8d666e020 | ||
|
|
49ed594253 | ||
|
|
144df6eecc | ||
|
|
dfcd13e51e |
@@ -36,13 +36,14 @@ describe('install', () => {
|
|||||||
process.env = originalEnv;
|
process.env = originalEnv;
|
||||||
});
|
});
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
test.each(['v24.0.5'])(
|
test.each(['v24.0.4'])(
|
||||||
'install docker %s', async (version) => {
|
'install docker %s', async (version) => {
|
||||||
await expect((async () => {
|
await expect((async () => {
|
||||||
const install = new Install({
|
const install = new Install({
|
||||||
version: version,
|
version: version,
|
||||||
runDir: tmpDir,
|
runDir: tmpDir,
|
||||||
contextName: 'foo'
|
contextName: 'foo',
|
||||||
|
daemonConfig: `{"debug":true,"features":{"containerd-snapshotter":true}}`
|
||||||
});
|
});
|
||||||
await install.download();
|
await install.download();
|
||||||
await install.install();
|
await install.install();
|
||||||
|
|||||||
@@ -54,14 +54,16 @@
|
|||||||
"@actions/tool-cache": "^2.0.1",
|
"@actions/tool-cache": "^2.0.1",
|
||||||
"@octokit/plugin-rest-endpoint-methods": "^7.2.3",
|
"@octokit/plugin-rest-endpoint-methods": "^7.2.3",
|
||||||
"async-retry": "^1.3.3",
|
"async-retry": "^1.3.3",
|
||||||
"csv-parse": "^5.4.0",
|
"csv-parse": "^5.5.0",
|
||||||
"handlebars": "^4.7.8",
|
"handlebars": "^4.7.8",
|
||||||
|
"js-yaml": "^4.1.0",
|
||||||
"jwt-decode": "^3.1.2",
|
"jwt-decode": "^3.1.2",
|
||||||
"semver": "^7.5.4",
|
"semver": "^7.5.4",
|
||||||
"tmp": "^0.2.1"
|
"tmp": "^0.2.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/csv-parse": "^1.2.2",
|
"@types/csv-parse": "^1.2.2",
|
||||||
|
"@types/js-yaml": "^4.0.5",
|
||||||
"@types/node": "^16.18.21",
|
"@types/node": "^16.18.21",
|
||||||
"@types/semver": "^7.5.0",
|
"@types/semver": "^7.5.0",
|
||||||
"@types/tmp": "^0.2.3",
|
"@types/tmp": "^0.2.3",
|
||||||
|
|||||||
@@ -17,10 +17,6 @@
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import {Context} from '../context';
|
import {Context} from '../context';
|
||||||
|
|
||||||
export const setupDockerLinuxSh = (): string => {
|
|
||||||
return get('docker-setup-linux.sh', setupDockerLinuxShData, '0755');
|
|
||||||
};
|
|
||||||
|
|
||||||
export const setupDockerWinPs1 = (): string => {
|
export const setupDockerWinPs1 = (): string => {
|
||||||
return get('docker-setup-win.ps1', setupDockerWinPs1Data);
|
return get('docker-setup-win.ps1', setupDockerWinPs1Data);
|
||||||
};
|
};
|
||||||
@@ -45,43 +41,6 @@ const get = (filename: string, data: string, mode?: string): string => {
|
|||||||
return assetPath;
|
return assetPath;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setupDockerLinuxShData = `
|
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -eu
|
|
||||||
|
|
||||||
: "\${TOOLDIR=}"
|
|
||||||
: "\${RUNDIR=}"
|
|
||||||
: "\${DOCKER_HOST=}"
|
|
||||||
|
|
||||||
export PATH="$TOOLDIR::$PATH"
|
|
||||||
|
|
||||||
if [ -z "$DOCKER_HOST" ]; then
|
|
||||||
echo >&2 'error: DOCKER_HOST required'
|
|
||||||
false
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! command -v dockerd &> /dev/null; then
|
|
||||||
echo >&2 'error: dockerd missing from PATH'
|
|
||||||
false
|
|
||||||
fi
|
|
||||||
|
|
||||||
mkdir -p "$RUNDIR"
|
|
||||||
|
|
||||||
(
|
|
||||||
echo "Starting dockerd"
|
|
||||||
set -x
|
|
||||||
exec dockerd \\
|
|
||||||
--debug \\
|
|
||||||
--host="$DOCKER_HOST" \\
|
|
||||||
--exec-root="$RUNDIR/execroot" \\
|
|
||||||
--data-root="$RUNDIR/data" \\
|
|
||||||
--pidfile="$RUNDIR/docker.pid" \\
|
|
||||||
--userland-proxy=false \\
|
|
||||||
2>&1 | tee "$RUNDIR/dockerd.log"
|
|
||||||
) &
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const setupDockerWinPs1Data = `
|
export const setupDockerWinPs1Data = `
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
param(
|
param(
|
||||||
@@ -92,7 +51,10 @@ param(
|
|||||||
[string]$RunDir,
|
[string]$RunDir,
|
||||||
|
|
||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $true)]
|
||||||
[string]$DockerHost)
|
[string]$DockerHost,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $false)]
|
||||||
|
[string]$DaemonConfig)
|
||||||
|
|
||||||
$pwver = (Get-ItemProperty -Path HKLM:\\SOFTWARE\\Microsoft\\PowerShell\\3\\PowerShellEngine -Name 'PowerShellVersion').PowerShellVersion
|
$pwver = (Get-ItemProperty -Path HKLM:\\SOFTWARE\\Microsoft\\PowerShell\\3\\PowerShellEngine -Name 'PowerShellVersion').PowerShellVersion
|
||||||
Write-Host "PowerShell version: $pwver"
|
Write-Host "PowerShell version: $pwver"
|
||||||
@@ -120,6 +82,12 @@ if (Get-Service docker -ErrorAction SilentlyContinue) {
|
|||||||
$env:DOCKER_HOST = $DockerHost
|
$env:DOCKER_HOST = $DockerHost
|
||||||
Write-Host "DOCKER_HOST: $env:DOCKER_HOST"
|
Write-Host "DOCKER_HOST: $env:DOCKER_HOST"
|
||||||
|
|
||||||
|
if ($DaemonConfig) {
|
||||||
|
Write-Host "Writing Docker daemon config"
|
||||||
|
New-Item -ItemType Directory -Force -Path "$env:ProgramData\\Docker\\config"
|
||||||
|
$DaemonConfig | Out-File -FilePath "$env:ProgramData\\Docker\\config\\daemon.json"
|
||||||
|
}
|
||||||
|
|
||||||
Write-Host "Creating service"
|
Write-Host "Creating service"
|
||||||
New-Item -ItemType Directory "$RunDir\\moby-root" -ErrorAction SilentlyContinue | Out-Null
|
New-Item -ItemType Directory "$RunDir\\moby-root" -ErrorAction SilentlyContinue | Out-Null
|
||||||
New-Item -ItemType Directory "$RunDir\\moby-exec" -ErrorAction SilentlyContinue | Out-Null
|
New-Item -ItemType Directory "$RunDir\\moby-exec" -ErrorAction SilentlyContinue | Out-Null
|
||||||
@@ -246,7 +214,7 @@ forwardAgent: false
|
|||||||
#
|
#
|
||||||
# Colima default behaviour: buildkit enabled
|
# Colima default behaviour: buildkit enabled
|
||||||
# Default: {}
|
# Default: {}
|
||||||
docker: {}
|
{{daemonConfig}}
|
||||||
|
|
||||||
# Virtual Machine type (qemu, vz)
|
# Virtual Machine type (qemu, vz)
|
||||||
# NOTE: this is macOS 13 only. For Linux and macOS <13.0, qemu is always used.
|
# NOTE: this is macOS 13 only. For Linux and macOS <13.0, qemu is always used.
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import fs from 'fs';
|
|||||||
import os from 'os';
|
import os from 'os';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import retry from 'async-retry';
|
import retry from 'async-retry';
|
||||||
|
import yaml from 'js-yaml';
|
||||||
import * as handlebars from 'handlebars';
|
import * as handlebars from 'handlebars';
|
||||||
import * as util from 'util';
|
import * as util from 'util';
|
||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
@@ -29,7 +30,7 @@ import * as tc from '@actions/tool-cache';
|
|||||||
import {Context} from '../context';
|
import {Context} from '../context';
|
||||||
import {Exec} from '../exec';
|
import {Exec} from '../exec';
|
||||||
import {Util} from '../util';
|
import {Util} from '../util';
|
||||||
import {colimaYamlData, dockerServiceLogsPs1, qemuEntitlements, setupDockerLinuxSh, setupDockerWinPs1} from './assets';
|
import {colimaYamlData, dockerServiceLogsPs1, qemuEntitlements, setupDockerWinPs1} from './assets';
|
||||||
import {GitHubRelease} from '../types/github';
|
import {GitHubRelease} from '../types/github';
|
||||||
|
|
||||||
export interface InstallOpts {
|
export interface InstallOpts {
|
||||||
@@ -37,6 +38,7 @@ export interface InstallOpts {
|
|||||||
channel?: string;
|
channel?: string;
|
||||||
runDir: string;
|
runDir: string;
|
||||||
contextName?: string;
|
contextName?: string;
|
||||||
|
daemonConfig?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Install {
|
export class Install {
|
||||||
@@ -44,6 +46,7 @@ export class Install {
|
|||||||
private readonly version: string;
|
private readonly version: string;
|
||||||
private readonly channel: string;
|
private readonly channel: string;
|
||||||
private readonly contextName: string;
|
private readonly contextName: string;
|
||||||
|
private readonly daemonConfig?: string;
|
||||||
private _version: string | undefined;
|
private _version: string | undefined;
|
||||||
private _toolDir: string | undefined;
|
private _toolDir: string | undefined;
|
||||||
|
|
||||||
@@ -52,6 +55,7 @@ export class Install {
|
|||||||
this.version = opts.version || 'latest';
|
this.version = opts.version || 'latest';
|
||||||
this.channel = opts.channel || 'stable';
|
this.channel = opts.channel || 'stable';
|
||||||
this.contextName = opts.contextName || 'setup-docker-action';
|
this.contextName = opts.contextName || 'setup-docker-action';
|
||||||
|
this.daemonConfig = opts.daemonConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
get toolDir(): string {
|
get toolDir(): string {
|
||||||
@@ -137,10 +141,15 @@ export class Install {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await core.group('Creating colima config', async () => {
|
await core.group('Creating colima config', async () => {
|
||||||
|
let daemonConfig = yaml.dump({docker: {}});
|
||||||
|
if (this.daemonConfig) {
|
||||||
|
daemonConfig = yaml.dump(yaml.load(JSON.stringify({docker: JSON.parse(this.daemonConfig)})));
|
||||||
|
}
|
||||||
const colimaCfg = handlebars.compile(colimaYamlData)({
|
const colimaCfg = handlebars.compile(colimaYamlData)({
|
||||||
hostArch: Install.platformArch(),
|
hostArch: Install.platformArch(),
|
||||||
dockerVersion: this._version,
|
dockerVersion: this._version,
|
||||||
dockerChannel: this.channel
|
dockerChannel: this.channel,
|
||||||
|
daemonConfig: daemonConfig
|
||||||
});
|
});
|
||||||
core.info(`Writing colima config to ${path.join(colimaDir, 'colima.yaml')}`);
|
core.info(`Writing colima config to ${path.join(colimaDir, 'colima.yaml')}`);
|
||||||
fs.writeFileSync(path.join(colimaDir, 'colima.yaml'), colimaCfg);
|
fs.writeFileSync(path.join(colimaDir, 'colima.yaml'), colimaCfg);
|
||||||
@@ -192,44 +201,65 @@ export class Install {
|
|||||||
const dockerHost = `unix://${path.join(this.runDir, 'docker.sock')}`;
|
const dockerHost = `unix://${path.join(this.runDir, 'docker.sock')}`;
|
||||||
await io.mkdirP(this.runDir);
|
await io.mkdirP(this.runDir);
|
||||||
|
|
||||||
|
const daemonConfigPath = path.join(this.runDir, 'daemon.json');
|
||||||
|
await fs.writeFileSync(daemonConfigPath, '{}');
|
||||||
|
|
||||||
|
let daemonConfig = undefined;
|
||||||
|
const daemonConfigDefaultPath = '/etc/docker/daemon.json';
|
||||||
|
if (fs.existsSync(daemonConfigDefaultPath)) {
|
||||||
|
await core.group('Default Docker daemon config found', async () => {
|
||||||
|
core.info(JSON.stringify(JSON.parse(fs.readFileSync(daemonConfigDefaultPath, {encoding: 'utf8'})), null, 2));
|
||||||
|
});
|
||||||
|
daemonConfig = JSON.parse(fs.readFileSync(daemonConfigDefaultPath, {encoding: 'utf8'}));
|
||||||
|
}
|
||||||
|
if (this.daemonConfig) {
|
||||||
|
daemonConfig = Object.assign(daemonConfig || {}, JSON.parse(this.daemonConfig));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (daemonConfig) {
|
||||||
|
const daemonConfigStr = JSON.stringify(daemonConfig, null, 2);
|
||||||
|
await core.group('Writing Docker daemon config', async () => {
|
||||||
|
fs.writeFileSync(daemonConfigPath, daemonConfigStr);
|
||||||
|
core.info(daemonConfigStr);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
await core.group('Start Docker daemon', async () => {
|
await core.group('Start Docker daemon', async () => {
|
||||||
const bashPath: string = await io.which('bash', true);
|
const bashPath: string = await io.which('bash', true);
|
||||||
const proc = await child_process.spawn(`sudo -E ${bashPath} ${setupDockerLinuxSh()}`, [], {
|
const cmd = `${this.toolDir}/dockerd --host="${dockerHost}" --config-file="${daemonConfigPath}" --exec-root="${this.runDir}/execroot" --data-root="${this.runDir}/data" --pidfile="${this.runDir}/docker.pid" --userland-proxy=false`;
|
||||||
detached: true,
|
core.info(`[command] ${cmd}`); // https://github.com/actions/toolkit/blob/3d652d3133965f63309e4b2e1c8852cdbdcb3833/packages/exec/src/toolrunner.ts#L47
|
||||||
shell: true,
|
const proc = await child_process.spawn(
|
||||||
stdio: ['ignore', process.stdout, process.stderr],
|
// We can't use Exec.exec here because we need to detach the process to
|
||||||
env: Object.assign({}, process.env, {
|
// avoid killing it when the action finishes running. Even if detached,
|
||||||
TOOLDIR: this.toolDir,
|
// we also need to run dockerd in a subshell and unref the process so
|
||||||
RUNDIR: this.runDir,
|
// GitHub Action doesn't wait for it to finish.
|
||||||
DOCKER_HOST: dockerHost
|
`sudo -E ${bashPath} << EOF
|
||||||
}) as {
|
( ${cmd} 2>&1 | tee "${this.runDir}/dockerd.log" ) &
|
||||||
[key: string]: string;
|
EOF`,
|
||||||
|
[],
|
||||||
|
{
|
||||||
|
detached: true,
|
||||||
|
shell: true,
|
||||||
|
stdio: ['ignore', process.stdout, process.stderr]
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
proc.unref();
|
proc.unref();
|
||||||
const retries = 20;
|
await Util.sleep(3);
|
||||||
|
const retries = 10;
|
||||||
await retry(
|
await retry(
|
||||||
async bail => {
|
async bail => {
|
||||||
await Exec.getExecOutput(`docker version`, undefined, {
|
try {
|
||||||
ignoreReturnCode: true,
|
await Exec.getExecOutput(`docker version`, undefined, {
|
||||||
silent: true,
|
silent: true,
|
||||||
env: Object.assign({}, process.env, {
|
env: Object.assign({}, process.env, {
|
||||||
DOCKER_HOST: dockerHost
|
DOCKER_HOST: dockerHost
|
||||||
}) as {
|
}) as {
|
||||||
[key: string]: string;
|
[key: string]: string;
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(res => {
|
|
||||||
if (res.stderr.length > 0 && res.exitCode != 0) {
|
|
||||||
bail(new Error(res.stderr));
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
return res.exitCode == 0;
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
bail(error);
|
|
||||||
return false;
|
|
||||||
});
|
});
|
||||||
|
} catch (e) {
|
||||||
|
bail(e);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
retries: retries,
|
retries: retries,
|
||||||
@@ -251,11 +281,32 @@ export class Install {
|
|||||||
private async installWindows(): Promise<void> {
|
private async installWindows(): Promise<void> {
|
||||||
const dockerHost = 'npipe:////./pipe/setup_docker_action';
|
const dockerHost = 'npipe:////./pipe/setup_docker_action';
|
||||||
|
|
||||||
|
let daemonConfig = undefined;
|
||||||
|
const daemonConfigPath = path.join(this.runDir, 'daemon.json');
|
||||||
|
if (fs.existsSync(daemonConfigPath)) {
|
||||||
|
await core.group('Default Docker daemon config found', async () => {
|
||||||
|
core.info(JSON.stringify(JSON.parse(fs.readFileSync(daemonConfigPath, {encoding: 'utf8'})), null, 2));
|
||||||
|
});
|
||||||
|
daemonConfig = JSON.parse(fs.readFileSync(daemonConfigPath, {encoding: 'utf8'}));
|
||||||
|
}
|
||||||
|
if (this.daemonConfig) {
|
||||||
|
daemonConfig = Object.assign(daemonConfig || {}, JSON.parse(this.daemonConfig));
|
||||||
|
}
|
||||||
|
|
||||||
|
let daemonConfigStr = '{}';
|
||||||
|
if (daemonConfig) {
|
||||||
|
daemonConfigStr = JSON.stringify(daemonConfig, null, 2);
|
||||||
|
await core.group('Docker daemon config', async () => {
|
||||||
|
core.info(daemonConfigStr);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
await core.group('Install Docker daemon service', async () => {
|
await core.group('Install Docker daemon service', async () => {
|
||||||
const setupCmd = await Util.powershellCommand(setupDockerWinPs1(), {
|
const setupCmd = await Util.powershellCommand(setupDockerWinPs1(), {
|
||||||
ToolDir: this.toolDir,
|
ToolDir: this.toolDir,
|
||||||
RunDir: this.runDir,
|
RunDir: this.runDir,
|
||||||
DockerHost: dockerHost
|
DockerHost: dockerHost,
|
||||||
|
DaemonConfig: daemonConfigStr
|
||||||
});
|
});
|
||||||
await Exec.exec(setupCmd.command, setupCmd.args);
|
await Exec.exec(setupCmd.command, setupCmd.args);
|
||||||
const logCmd = await Util.powershellCommand(dockerServiceLogsPs1());
|
const logCmd = await Util.powershellCommand(dockerServiceLogsPs1());
|
||||||
|
|||||||
20
yarn.lock
20
yarn.lock
@@ -974,6 +974,7 @@ __metadata:
|
|||||||
"@actions/tool-cache": ^2.0.1
|
"@actions/tool-cache": ^2.0.1
|
||||||
"@octokit/plugin-rest-endpoint-methods": ^7.2.3
|
"@octokit/plugin-rest-endpoint-methods": ^7.2.3
|
||||||
"@types/csv-parse": ^1.2.2
|
"@types/csv-parse": ^1.2.2
|
||||||
|
"@types/js-yaml": ^4.0.5
|
||||||
"@types/node": ^16.18.21
|
"@types/node": ^16.18.21
|
||||||
"@types/semver": ^7.5.0
|
"@types/semver": ^7.5.0
|
||||||
"@types/tmp": ^0.2.3
|
"@types/tmp": ^0.2.3
|
||||||
@@ -981,7 +982,7 @@ __metadata:
|
|||||||
"@typescript-eslint/parser": ^5.56.0
|
"@typescript-eslint/parser": ^5.56.0
|
||||||
async-retry: ^1.3.3
|
async-retry: ^1.3.3
|
||||||
cpy-cli: ^4.2.0
|
cpy-cli: ^4.2.0
|
||||||
csv-parse: ^5.4.0
|
csv-parse: ^5.5.0
|
||||||
dotenv: ^16.0.3
|
dotenv: ^16.0.3
|
||||||
eslint: ^8.36.0
|
eslint: ^8.36.0
|
||||||
eslint-config-prettier: ^8.8.0
|
eslint-config-prettier: ^8.8.0
|
||||||
@@ -990,6 +991,7 @@ __metadata:
|
|||||||
eslint-plugin-prettier: ^4.2.1
|
eslint-plugin-prettier: ^4.2.1
|
||||||
handlebars: ^4.7.8
|
handlebars: ^4.7.8
|
||||||
jest: ^29.5.0
|
jest: ^29.5.0
|
||||||
|
js-yaml: ^4.1.0
|
||||||
jwt-decode: ^3.1.2
|
jwt-decode: ^3.1.2
|
||||||
prettier: ^2.8.7
|
prettier: ^2.8.7
|
||||||
rimraf: ^4.4.1
|
rimraf: ^4.4.1
|
||||||
@@ -1825,6 +1827,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@types/js-yaml@npm:^4.0.5":
|
||||||
|
version: 4.0.5
|
||||||
|
resolution: "@types/js-yaml@npm:4.0.5"
|
||||||
|
checksum: 7dcac8c50fec31643cc9d6444b5503239a861414cdfaa7ae9a38bc22597c4d850c4b8cec3d82d73b3fbca408348ce223b0408d598b32e094470dfffc6d486b4d
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@types/json-schema@npm:^7.0.9":
|
"@types/json-schema@npm:^7.0.9":
|
||||||
version: 7.0.9
|
version: 7.0.9
|
||||||
resolution: "@types/json-schema@npm:7.0.9"
|
resolution: "@types/json-schema@npm:7.0.9"
|
||||||
@@ -2876,13 +2885,20 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"csv-parse@npm:*, csv-parse@npm:^5.4.0":
|
"csv-parse@npm:*":
|
||||||
version: 5.4.0
|
version: 5.4.0
|
||||||
resolution: "csv-parse@npm:5.4.0"
|
resolution: "csv-parse@npm:5.4.0"
|
||||||
checksum: e9c2f23953f628849c476adeb9f500e9c03fcfe98fea7b732366e3c2ed0b07a5e67f32ca12a0132db6eb71601c080b4935b5921296db3625a597e24caa16f633
|
checksum: e9c2f23953f628849c476adeb9f500e9c03fcfe98fea7b732366e3c2ed0b07a5e67f32ca12a0132db6eb71601c080b4935b5921296db3625a597e24caa16f633
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"csv-parse@npm:^5.5.0":
|
||||||
|
version: 5.5.0
|
||||||
|
resolution: "csv-parse@npm:5.5.0"
|
||||||
|
checksum: a9cc73a332debdabe9658c532e7668c626c06616113b650fb2e063f3a1451d45a48080c1942a680e8ddeaf1665efef49f126e655c54c19e025d70cccc002ff4d
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1":
|
"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1":
|
||||||
version: 4.3.1
|
version: 4.3.1
|
||||||
resolution: "debug@npm:4.3.1"
|
resolution: "debug@npm:4.3.1"
|
||||||
|
|||||||
Reference in New Issue
Block a user