mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-12 04:22:14 +00:00
Compare commits
13 Commits
vercel-plu
...
vercel-plu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4aa6a13912 | ||
|
|
81ea0082f1 | ||
|
|
6dff0875f5 | ||
|
|
30aa392c0a | ||
|
|
c4fc060030 | ||
|
|
3fa08bf64f | ||
|
|
43056bde1f | ||
|
|
a49966b9b4 | ||
|
|
7f55de71bb | ||
|
|
db8e36e04c | ||
|
|
82924bb5c4 | ||
|
|
18b5fac93e | ||
|
|
a6012e600b |
2
.github/workflows/test-integration-dev.yml
vendored
2
.github/workflows/test-integration-dev.yml
vendored
@@ -11,7 +11,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
name: Dev
|
name: Dev
|
||||||
timeout-minutes: 60
|
timeout-minutes: 75
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -27,3 +27,4 @@ test/lib/deployment/failed-page.txt
|
|||||||
/public
|
/public
|
||||||
__pycache__
|
__pycache__
|
||||||
.vercel
|
.vercel
|
||||||
|
.output
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "vercel",
|
"name": "vercel",
|
||||||
"version": "23.1.3-canary.30",
|
"version": "23.1.3-canary.34",
|
||||||
"preferGlobal": true,
|
"preferGlobal": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"description": "The command-line interface for Vercel",
|
"description": "The command-line interface for Vercel",
|
||||||
@@ -44,13 +44,13 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vercel/build-utils": "2.12.3-canary.18",
|
"@vercel/build-utils": "2.12.3-canary.18",
|
||||||
"@vercel/go": "1.2.4-canary.3",
|
"@vercel/go": "1.2.4-canary.4",
|
||||||
"@vercel/node": "1.12.2-canary.6",
|
"@vercel/node": "1.12.2-canary.6",
|
||||||
"@vercel/python": "2.0.6-canary.5",
|
"@vercel/python": "2.0.6-canary.6",
|
||||||
"@vercel/ruby": "1.2.8-canary.4",
|
"@vercel/ruby": "1.2.8-canary.4",
|
||||||
"update-notifier": "4.1.0",
|
"update-notifier": "4.1.0",
|
||||||
"vercel-plugin-middleware": "0.0.0-canary.7",
|
"vercel-plugin-middleware": "0.0.0-canary.7",
|
||||||
"vercel-plugin-node": "1.12.2-canary.8"
|
"vercel-plugin-node": "1.12.2-canary.9"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@next/env": "11.1.2",
|
"@next/env": "11.1.2",
|
||||||
|
|||||||
@@ -169,15 +169,15 @@ export default async function main(client: Client) {
|
|||||||
|
|
||||||
const buildState = { ...project.settings };
|
const buildState = { ...project.settings };
|
||||||
|
|
||||||
client.output.log(`Retrieved Project Settings:`);
|
console.log(`Retrieved Project Settings:`);
|
||||||
client.output.print(
|
console.log(
|
||||||
chalk.dim(` - ${chalk.bold(`Framework Preset:`)} ${framework.name}\n`)
|
chalk.dim(` - ${chalk.bold(`Framework Preset:`)} ${framework.name}`)
|
||||||
);
|
);
|
||||||
|
|
||||||
for (let field of fields) {
|
for (let field of fields) {
|
||||||
const defaults = (framework.settings as any)[field.value];
|
const defaults = (framework.settings as any)[field.value];
|
||||||
if (defaults) {
|
if (defaults) {
|
||||||
client.output.print(
|
console.log(
|
||||||
chalk.dim(
|
chalk.dim(
|
||||||
` - ${chalk.bold(`${field.name}:`)} ${`${
|
` - ${chalk.bold(`${field.name}:`)} ${`${
|
||||||
project.settings[field.value]
|
project.settings[field.value]
|
||||||
@@ -185,7 +185,7 @@ export default async function main(client: Client) {
|
|||||||
: isSettingValue(defaults)
|
: isSettingValue(defaults)
|
||||||
? defaults.value
|
? defaults.value
|
||||||
: chalk.italic(`${defaults.placeholder}`)
|
: chalk.italic(`${defaults.placeholder}`)
|
||||||
}`}\n`
|
}`}`
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -201,14 +201,14 @@ export default async function main(client: Client) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (loadedEnvFiles.length > 0) {
|
if (loadedEnvFiles.length > 0) {
|
||||||
client.output.log(
|
console.log(
|
||||||
`Loaded Environment Variables from ${loadedEnvFiles.length} ${pluralize(
|
`Loaded Environment Variables from ${loadedEnvFiles.length} ${pluralize(
|
||||||
'file',
|
'file',
|
||||||
loadedEnvFiles.length
|
loadedEnvFiles.length
|
||||||
)}:`
|
)}:`
|
||||||
);
|
);
|
||||||
for (let envFile of loadedEnvFiles) {
|
for (let envFile of loadedEnvFiles) {
|
||||||
client.output.print(chalk.dim(` - ${envFile.path}\n`));
|
console.log(chalk.dim(` - ${envFile.path}`));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,7 +239,7 @@ export default async function main(client: Client) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (plugins?.pluginCount && plugins?.pluginCount > 0) {
|
if (plugins?.pluginCount && plugins?.pluginCount > 0) {
|
||||||
client.output.log(
|
console.log(
|
||||||
`Loaded ${plugins.pluginCount} CLI ${pluralize(
|
`Loaded ${plugins.pluginCount} CLI ${pluralize(
|
||||||
'Plugin',
|
'Plugin',
|
||||||
plugins.pluginCount
|
plugins.pluginCount
|
||||||
@@ -247,7 +247,7 @@ export default async function main(client: Client) {
|
|||||||
);
|
);
|
||||||
// preBuild Plugins
|
// preBuild Plugins
|
||||||
if (plugins.preBuildPlugins.length > 0) {
|
if (plugins.preBuildPlugins.length > 0) {
|
||||||
client.output.log(
|
console.log(
|
||||||
`Running ${plugins.pluginCount} CLI ${pluralize(
|
`Running ${plugins.pluginCount} CLI ${pluralize(
|
||||||
'Plugin',
|
'Plugin',
|
||||||
plugins.pluginCount
|
plugins.pluginCount
|
||||||
@@ -286,7 +286,7 @@ export default async function main(client: Client) {
|
|||||||
fs.removeSync(join(cwd, OUTPUT_DIR));
|
fs.removeSync(join(cwd, OUTPUT_DIR));
|
||||||
|
|
||||||
if (typeof buildState.buildCommand === 'string') {
|
if (typeof buildState.buildCommand === 'string') {
|
||||||
client.output.log(`Running Build Command: ${cmd(buildState.buildCommand)}`);
|
console.log(`Running Build Command: ${cmd(buildState.buildCommand)}`);
|
||||||
await execCommand(buildState.buildCommand, {
|
await execCommand(buildState.buildCommand, {
|
||||||
...spawnOpts,
|
...spawnOpts,
|
||||||
// Yarn v2 PnP mode may be activated, so force
|
// Yarn v2 PnP mode may be activated, so force
|
||||||
@@ -352,7 +352,7 @@ export default async function main(client: Client) {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
client.output.stopSpinner();
|
client.output.stopSpinner();
|
||||||
client.output.log(
|
console.log(
|
||||||
`Copied ${files.length.toLocaleString()} files from ${param(
|
`Copied ${files.length.toLocaleString()} files from ${param(
|
||||||
distDir
|
distDir
|
||||||
)} to ${param(outputDir)} ${copyStamp()}`
|
)} to ${param(outputDir)} ${copyStamp()}`
|
||||||
@@ -573,7 +573,7 @@ export default async function main(client: Client) {
|
|||||||
|
|
||||||
// Build Plugins
|
// Build Plugins
|
||||||
if (plugins?.buildPlugins && plugins.buildPlugins.length > 0) {
|
if (plugins?.buildPlugins && plugins.buildPlugins.length > 0) {
|
||||||
client.output.log(
|
console.log(
|
||||||
`Running ${plugins.pluginCount} CLI ${pluralize(
|
`Running ${plugins.pluginCount} CLI ${pluralize(
|
||||||
'Plugin',
|
'Plugin',
|
||||||
plugins.pluginCount
|
plugins.pluginCount
|
||||||
@@ -608,13 +608,13 @@ export default async function main(client: Client) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
client.output.print(
|
console.log(
|
||||||
`${prependEmoji(
|
`${prependEmoji(
|
||||||
`Build Completed in ${chalk.bold(OUTPUT_DIR)} ${chalk.gray(
|
`Build Completed in ${chalk.bold(OUTPUT_DIR)} ${chalk.gray(
|
||||||
buildStamp()
|
buildStamp()
|
||||||
)}`,
|
)}`,
|
||||||
emoji('success')
|
emoji('success')
|
||||||
)}\n`
|
)}`
|
||||||
);
|
);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -660,25 +660,37 @@ export async function runPackageJsonScript(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
client.output.log(`Running Build Command: ${cmd(opts.prettyCommand)}\n`);
|
console.log(`Running Build Command: ${cmd(opts.prettyCommand)}\n`);
|
||||||
await spawnAsync(cliType, ['run', scriptName], opts);
|
await spawnAsync(cliType, ['run', scriptName], opts);
|
||||||
client.output.print('\n'); // give it some room
|
console.log(); // give it some room
|
||||||
client.output.debug(`Script complete [${Date.now() - runScriptTime}ms]`);
|
client.output.debug(`Script complete [${Date.now() - runScriptTime}ms]`);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function linkOrCopy(existingPath: string, newPath: string) {
|
async function linkOrCopy(existingPath: string, newPath: string) {
|
||||||
try {
|
try {
|
||||||
await fs.createLink(existingPath, newPath);
|
if (
|
||||||
|
newPath.endsWith('.nft.json') ||
|
||||||
|
newPath.endsWith('middleware-manifest.json') ||
|
||||||
|
newPath.endsWith('required-server-files.json')
|
||||||
|
) {
|
||||||
|
await fs.copy(existingPath, newPath, {
|
||||||
|
overwrite: true,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await fs.createSymlink(existingPath, newPath, 'file');
|
||||||
|
}
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
// eslint-disable-line
|
// eslint-disable-line
|
||||||
// If a hard link to the same file already exists
|
// If a symlink to the same file already exists
|
||||||
// then trying to copy it will make an empty file from it.
|
// then trying to copy it will make an empty file from it.
|
||||||
if (err['code'] === 'EEXIST') return;
|
if (err['code'] === 'EEXIST') return;
|
||||||
// In some VERY rare cases (1 in a thousand), hard-link creation fails on Windows.
|
// In some VERY rare cases (1 in a thousand), symlink creation fails on Windows.
|
||||||
// In that case, we just fall back to copying.
|
// In that case, we just fall back to copying.
|
||||||
// This issue is reproducible with "pnpm add @material-ui/icons@4.9.1"
|
// This issue is reproducible with "pnpm add @material-ui/icons@4.9.1"
|
||||||
await fs.copyFile(existingPath, newPath);
|
await fs.copy(existingPath, newPath, {
|
||||||
|
overwrite: true,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -160,24 +160,26 @@ const main = async () => {
|
|||||||
// * a path to deploy (as in: `vercel path/`)
|
// * a path to deploy (as in: `vercel path/`)
|
||||||
// * a subcommand (as in: `vercel ls`)
|
// * a subcommand (as in: `vercel ls`)
|
||||||
const targetOrSubcommand = argv._[2];
|
const targetOrSubcommand = argv._[2];
|
||||||
|
const isBuildOrDev =
|
||||||
|
targetOrSubcommand === 'build' || targetOrSubcommand === 'dev';
|
||||||
|
|
||||||
|
if (isBuildOrDev) {
|
||||||
|
console.log(
|
||||||
|
`${chalk.grey(
|
||||||
|
`${getTitleName()} CLI ${
|
||||||
|
pkg.version
|
||||||
|
} ${targetOrSubcommand} (beta) — https://vercel.com/feedback`
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
output.print(
|
output.print(
|
||||||
`${chalk.grey(
|
`${chalk.grey(
|
||||||
`${getTitleName()} CLI ${pkg.version}${
|
`${getTitleName()} CLI ${pkg.version}${
|
||||||
targetOrSubcommand === 'dev'
|
isCanary ? ' — https://vercel.com/feedback' : ''
|
||||||
? ' dev (beta)'
|
|
||||||
: targetOrSubcommand === 'build'
|
|
||||||
? ' build (beta)'
|
|
||||||
: ''
|
|
||||||
}${
|
|
||||||
isCanary ||
|
|
||||||
targetOrSubcommand === 'dev' ||
|
|
||||||
targetOrSubcommand === 'build'
|
|
||||||
? ' — https://vercel.com/feedback'
|
|
||||||
: ''
|
|
||||||
}`
|
}`
|
||||||
)}\n`
|
)}\n`
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Handle `--version` directly
|
// Handle `--version` directly
|
||||||
if (!targetOrSubcommand && argv['--version']) {
|
if (!targetOrSubcommand && argv['--version']) {
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
import { Stats } from 'fs';
|
import { Stats } from 'fs';
|
||||||
import { sep, dirname, join, resolve } from 'path';
|
import { sep, dirname, join, resolve } from 'path';
|
||||||
import { readJSON, lstat, readlink, readFile, realpath } from 'fs-extra';
|
import { lstat, readlink, readFile, realpath } from 'fs-extra';
|
||||||
import { isCanary } from './is-canary';
|
import { isCanary } from './is-canary';
|
||||||
import { getPkgName } from './pkg-name';
|
import { getPkgName } from './pkg-name';
|
||||||
|
|
||||||
// `npm` tacks a bunch of extra properties on the `package.json` file,
|
|
||||||
// so check for one of them to determine yarn vs. npm.
|
|
||||||
async function isYarn(): Promise<boolean> {
|
async function isYarn(): Promise<boolean> {
|
||||||
let s: Stats;
|
let s: Stats;
|
||||||
let binPath = process.argv[1];
|
let binPath = process.argv[1];
|
||||||
@@ -20,8 +18,12 @@ async function isYarn(): Promise<boolean> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const pkgPath = join(dirname(binPath), '..', 'package.json');
|
const pkgPath = join(dirname(binPath), '..', 'package.json');
|
||||||
const pkg = await readJSON(pkgPath).catch(() => ({}));
|
/*
|
||||||
return !('_id' in pkg);
|
* Generally, pkgPath looks like:
|
||||||
|
* "/Users/username/.config/yarn/global/node_modules/vercel/package.json"
|
||||||
|
* "/usr/local/share/.config/yarn/global/node_modules/vercel/package.json"
|
||||||
|
*/
|
||||||
|
return pkgPath.includes(join('yarn', 'global'));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getConfigPrefix() {
|
async function getConfigPrefix() {
|
||||||
|
|||||||
@@ -125,11 +125,15 @@ export class Output {
|
|||||||
this.debug(`Spinner invoked (${message}) with a ${delay}ms delay`);
|
this.debug(`Spinner invoked (${message}) with a ${delay}ms delay`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (this.isTTY) {
|
||||||
if (this._spinner) {
|
if (this._spinner) {
|
||||||
this._spinner.text = message;
|
this._spinner.text = message;
|
||||||
} else {
|
} else {
|
||||||
this._spinner = wait(message, delay);
|
this._spinner = wait(message, delay);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
this.print(`${message}\n`);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
stopSpinner = () => {
|
stopSpinner = () => {
|
||||||
|
|||||||
@@ -244,20 +244,27 @@ export async function linkFolderToProject(
|
|||||||
try {
|
try {
|
||||||
const gitIgnorePath = join(path, '.gitignore');
|
const gitIgnorePath = join(path, '.gitignore');
|
||||||
|
|
||||||
const gitIgnore = await readFile(gitIgnorePath, 'utf8').catch(() => null);
|
let gitIgnore =
|
||||||
const EOL = gitIgnore && gitIgnore.includes('\r\n') ? '\r\n' : os.EOL;
|
(await readFile(gitIgnorePath, 'utf8').catch(() => null)) ?? '';
|
||||||
|
const EOL = gitIgnore.includes('\r\n') ? '\r\n' : os.EOL;
|
||||||
|
let contentModified = false;
|
||||||
|
|
||||||
if (
|
if (!gitIgnore.split(EOL).includes(VERCEL_DIR)) {
|
||||||
!gitIgnore ||
|
gitIgnore += `${
|
||||||
!gitIgnore.split(EOL).includes(VERCEL_DIR) ||
|
gitIgnore.endsWith(EOL) || gitIgnore.length === 0 ? '' : EOL
|
||||||
!gitIgnore.split(EOL).includes(VERCEL_OUTPUT_DIR)
|
}${VERCEL_DIR}${EOL}`;
|
||||||
) {
|
contentModified = true;
|
||||||
await writeFile(
|
}
|
||||||
gitIgnorePath,
|
|
||||||
gitIgnore
|
if (!gitIgnore.split(EOL).includes(VERCEL_OUTPUT_DIR)) {
|
||||||
? `${gitIgnore}${EOL}${VERCEL_DIR}${EOL}${VERCEL_OUTPUT_DIR}${EOL}`
|
gitIgnore += `${
|
||||||
: `${VERCEL_DIR}${EOL}${VERCEL_OUTPUT_DIR}${EOL}`
|
gitIgnore.endsWith(EOL) || gitIgnore.length === 0 ? '' : EOL
|
||||||
);
|
}${VERCEL_OUTPUT_DIR}${EOL}`;
|
||||||
|
contentModified = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contentModified) {
|
||||||
|
await writeFile(gitIgnorePath, gitIgnore);
|
||||||
isGitIgnoreUpdated = true;
|
isGitIgnoreUpdated = true;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -365,6 +365,10 @@ module.exports = async function prepare(session, binaryPath) {
|
|||||||
'project-link-dev': {
|
'project-link-dev': {
|
||||||
'package.json': '{}',
|
'package.json': '{}',
|
||||||
},
|
},
|
||||||
|
'project-link-gitignore': {
|
||||||
|
'package.json': '{}',
|
||||||
|
'.gitignore': '.output',
|
||||||
|
},
|
||||||
'project-link-legacy': {
|
'project-link-legacy': {
|
||||||
'index.html': 'Hello',
|
'index.html': 'Hello',
|
||||||
'vercel.json': '{"builds":[{"src":"*.html","use":"@vercel/static"}]}',
|
'vercel.json': '{"builds":[{"src":"*.html","use":"@vercel/static"}]}',
|
||||||
|
|||||||
23
packages/cli/test/integration.js
vendored
23
packages/cli/test/integration.js
vendored
@@ -3453,6 +3453,29 @@ test('[vc link --confirm] should not show prompts and autolink', async t => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('[vc link] should not duplicate paths in .gitignore', async t => {
|
||||||
|
const dir = fixture('project-link-gitignore');
|
||||||
|
|
||||||
|
// remove previously linked project if it exists
|
||||||
|
await remove(path.join(dir, '.vercel'));
|
||||||
|
|
||||||
|
const { exitCode, stderr, stdout } = await execa(
|
||||||
|
binaryPath,
|
||||||
|
['link', '--confirm', ...defaultArgs],
|
||||||
|
{ cwd: dir, reject: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
// Ensure the exit code is right
|
||||||
|
t.is(exitCode, 0, formatOutput({ stderr, stdout }));
|
||||||
|
|
||||||
|
// Ensure the message is correct pattern
|
||||||
|
t.regex(stderr, /Linked to /m);
|
||||||
|
|
||||||
|
// Ensure .gitignore is created
|
||||||
|
const gitignore = await readFile(path.join(dir, '.gitignore'), 'utf8');
|
||||||
|
t.is(gitignore, '.output\n.vercel\n');
|
||||||
|
});
|
||||||
|
|
||||||
test('[vc dev] should show prompts to set up project', async t => {
|
test('[vc dev] should show prompts to set up project', async t => {
|
||||||
const dir = fixture('project-link-dev');
|
const dir = fixture('project-link-dev');
|
||||||
const port = 58352;
|
const port = 58352;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ describe('getUpdateCommand', () => {
|
|||||||
it('should detect update command', async () => {
|
it('should detect update command', async () => {
|
||||||
const updateCommand = await getUpdateCommand();
|
const updateCommand = await getUpdateCommand();
|
||||||
expect(updateCommand).toEqual(
|
expect(updateCommand).toEqual(
|
||||||
`yarn add vercel@${isCanary() ? 'canary' : 'latest'}`
|
`npm i vercel@${isCanary() ? 'canary' : 'latest'}`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ import { join } from 'path';
|
|||||||
import stringArgv from 'string-argv';
|
import stringArgv from 'string-argv';
|
||||||
import { debug } from '@vercel/build-utils';
|
import { debug } from '@vercel/build-utils';
|
||||||
const versionMap = new Map([
|
const versionMap = new Map([
|
||||||
['1.16', '1.16'],
|
['1.17', '1.17.3'],
|
||||||
|
['1.16', '1.16.10'],
|
||||||
['1.15', '1.15.8'],
|
['1.15', '1.15.8'],
|
||||||
['1.14', '1.14.15'],
|
['1.14', '1.14.15'],
|
||||||
['1.13', '1.13.15'],
|
['1.13', '1.13.15'],
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vercel/go",
|
"name": "@vercel/go",
|
||||||
"version": "1.2.4-canary.3",
|
"version": "1.2.4-canary.4",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "./dist/index",
|
"main": "./dist/index",
|
||||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/go",
|
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/go",
|
||||||
|
|||||||
@@ -5,10 +5,10 @@
|
|||||||
{ "src": "subdirectory/index.go", "use": "@vercel/go" }
|
{ "src": "subdirectory/index.go", "use": "@vercel/go" }
|
||||||
],
|
],
|
||||||
"probes": [
|
"probes": [
|
||||||
{ "path": "/", "mustContain": "cow:go1.16:RANDOMNESS_PLACEHOLDER" },
|
{ "path": "/", "mustContain": "cow:go1.17.3:RANDOMNESS_PLACEHOLDER" },
|
||||||
{
|
{
|
||||||
"path": "/subdirectory",
|
"path": "/subdirectory",
|
||||||
"mustContain": "subcow:go1.16:RANDOMNESS_PLACEHOLDER"
|
"mustContain": "subcow:go1.17.3:RANDOMNESS_PLACEHOLDER"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
module with-nested
|
module with-nested
|
||||||
|
|
||||||
go 1.12
|
go 1.16
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"private": false,
|
"private": false,
|
||||||
"name": "vercel-plugin-go",
|
"name": "vercel-plugin-go",
|
||||||
"version": "1.0.0-canary.2",
|
"version": "1.0.0-canary.3",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"files": [
|
"files": [
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vercel/build-utils": "2.12.3-canary.18",
|
"@vercel/build-utils": "2.12.3-canary.18",
|
||||||
"@vercel/go": "1.2.4-canary.3"
|
"@vercel/go": "1.2.4-canary.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "vercel-plugin-node",
|
"name": "vercel-plugin-node",
|
||||||
"version": "1.12.2-canary.8",
|
"version": "1.12.2-canary.9",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "./dist/index",
|
"main": "./dist/index",
|
||||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/node-js",
|
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/node-js",
|
||||||
@@ -33,8 +33,9 @@
|
|||||||
"@types/jest": "27.0.2",
|
"@types/jest": "27.0.2",
|
||||||
"@types/node-fetch": "2",
|
"@types/node-fetch": "2",
|
||||||
"@types/test-listen": "1.1.0",
|
"@types/test-listen": "1.1.0",
|
||||||
|
"@types/yazl": "2.4.2",
|
||||||
"@vercel/build-utils": "2.12.3-canary.18",
|
"@vercel/build-utils": "2.12.3-canary.18",
|
||||||
"@vercel/fun": "1.0.1",
|
"@vercel/fun": "1.0.3",
|
||||||
"@vercel/ncc": "0.24.0",
|
"@vercel/ncc": "0.24.0",
|
||||||
"@vercel/nft": "0.14.0",
|
"@vercel/nft": "0.14.0",
|
||||||
"@vercel/node-bridge": "2.1.1-canary.2",
|
"@vercel/node-bridge": "2.1.1-canary.2",
|
||||||
@@ -47,7 +48,8 @@
|
|||||||
"node-fetch": "2",
|
"node-fetch": "2",
|
||||||
"source-map-support": "0.5.12",
|
"source-map-support": "0.5.12",
|
||||||
"test-listen": "1.1.0",
|
"test-listen": "1.1.0",
|
||||||
"ts-morph": "12.0.0"
|
"ts-morph": "12.0.0",
|
||||||
|
"yazl": "2.5.1"
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
"preset": "ts-jest",
|
"preset": "ts-jest",
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
readlinkSync,
|
readlinkSync,
|
||||||
statSync,
|
statSync,
|
||||||
promises as fsp,
|
promises as fsp,
|
||||||
|
existsSync,
|
||||||
} from 'fs';
|
} from 'fs';
|
||||||
import {
|
import {
|
||||||
basename,
|
basename,
|
||||||
@@ -44,6 +45,7 @@ import { AbortController } from 'abort-controller';
|
|||||||
import { Register, register } from './typescript';
|
import { Register, register } from './typescript';
|
||||||
import { pageToRoute } from './router/page-to-route';
|
import { pageToRoute } from './router/page-to-route';
|
||||||
import { isDynamicRoute } from './router/is-dynamic';
|
import { isDynamicRoute } from './router/is-dynamic';
|
||||||
|
import crypto from 'crypto';
|
||||||
|
|
||||||
export { shouldServe };
|
export { shouldServe };
|
||||||
export {
|
export {
|
||||||
@@ -118,9 +120,14 @@ async function downloadInstallAndBundle({
|
|||||||
const nodeVersion = await getNodeVersion(entrypointFsDirname);
|
const nodeVersion = await getNodeVersion(entrypointFsDirname);
|
||||||
const spawnOpts = getSpawnOptions({}, nodeVersion);
|
const spawnOpts = getSpawnOptions({}, nodeVersion);
|
||||||
|
|
||||||
|
// Only run when `package.json` exists.
|
||||||
|
if (existsSync(join(entrypointFsDirname, 'package.json'))) {
|
||||||
const installTime = Date.now();
|
const installTime = Date.now();
|
||||||
await runNpmInstall(entrypointFsDirname, [], spawnOpts, {}, nodeVersion);
|
await runNpmInstall(entrypointFsDirname, [], spawnOpts, {}, nodeVersion);
|
||||||
debug(`Install complete [${Date.now() - installTime}ms]`);
|
debug(`Install complete [${Date.now() - installTime}ms]`);
|
||||||
|
} else {
|
||||||
|
debug(`Skip install command for \`vercel-plugin-node\`.`);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
nodeVersion,
|
nodeVersion,
|
||||||
@@ -384,20 +391,23 @@ export async function buildEntrypoint({
|
|||||||
entrypoint: string;
|
entrypoint: string;
|
||||||
config: FunctionConfig;
|
config: FunctionConfig;
|
||||||
}) {
|
}) {
|
||||||
|
// Unique hash that will be used as directory name for `.output`.
|
||||||
|
const entrypointHash = crypto
|
||||||
|
.createHash('sha256')
|
||||||
|
.update(entrypoint)
|
||||||
|
.digest('hex');
|
||||||
const outputDirPath = join(workPath, '.output');
|
const outputDirPath = join(workPath, '.output');
|
||||||
|
|
||||||
const { dir, name } = parsePath(entrypoint);
|
const { dir, name } = parsePath(entrypoint);
|
||||||
const entrypointWithoutExt = join('/', dir, name);
|
const entrypointWithoutExt = join('/', dir, name);
|
||||||
const entrypointWithoutExtIndex = join(
|
const outputWorkPath = join(outputDirPath, 'inputs', entrypointHash);
|
||||||
dir,
|
const pagesDir = join(outputDirPath, 'server', 'pages');
|
||||||
name,
|
const pageOutput = join(pagesDir, renameTStoJS(entrypoint));
|
||||||
name === 'index' ? '' : 'index'
|
const nftOutput = `${pageOutput}.nft.json`;
|
||||||
);
|
|
||||||
const outputWorkPath = join(
|
|
||||||
outputDirPath,
|
|
||||||
'server/pages',
|
|
||||||
entrypointWithoutExtIndex
|
|
||||||
);
|
|
||||||
await fsp.mkdir(outputWorkPath, { recursive: true });
|
await fsp.mkdir(outputWorkPath, { recursive: true });
|
||||||
|
await fsp.mkdir(parsePath(pageOutput).dir, { recursive: true });
|
||||||
|
|
||||||
console.log(`Compiling "${entrypoint}" to "${outputWorkPath}"`);
|
console.log(`Compiling "${entrypoint}" to "${outputWorkPath}"`);
|
||||||
|
|
||||||
const shouldAddHelpers =
|
const shouldAddHelpers =
|
||||||
@@ -471,6 +481,8 @@ export async function buildEntrypoint({
|
|||||||
...launcherFiles,
|
...launcherFiles,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const nftFiles: { input: string; output: string }[] = [];
|
||||||
|
|
||||||
for (const filename of Object.keys(files)) {
|
for (const filename of Object.keys(files)) {
|
||||||
const outPath = join(outputWorkPath, filename);
|
const outPath = join(outputWorkPath, filename);
|
||||||
const file = files[filename];
|
const file = files[filename];
|
||||||
@@ -481,10 +493,36 @@ export async function buildEntrypoint({
|
|||||||
const finishPromise = once(ws, 'finish');
|
const finishPromise = once(ws, 'finish');
|
||||||
file.toStream().pipe(ws);
|
file.toStream().pipe(ws);
|
||||||
await finishPromise;
|
await finishPromise;
|
||||||
|
|
||||||
|
// The `handler` will be `.output/server/pages/api/subdirectory/___vc/__launcher.launcher`
|
||||||
|
// or `.output/server/pages/api/___vc/__launcher.launcher`.
|
||||||
|
// This means everything has to be mounted to the `dirname` of the entrypoint.
|
||||||
|
nftFiles.push({
|
||||||
|
input: relative(dirname(nftOutput), outPath),
|
||||||
|
output: join('.output', 'server', 'pages', dirname(entrypoint), filename),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await fsp.writeFile(
|
||||||
|
nftOutput,
|
||||||
|
JSON.stringify({
|
||||||
|
version: 1,
|
||||||
|
files: nftFiles,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
await fsp.copyFile(
|
||||||
|
join(outputWorkPath, renameTStoJS(entrypoint)),
|
||||||
|
pageOutput
|
||||||
|
);
|
||||||
|
|
||||||
|
let pageKey = relative(pagesDir, pageOutput);
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
pageKey = pageKey.replace(/\\/gm, '/');
|
||||||
}
|
}
|
||||||
|
|
||||||
const pages = {
|
const pages = {
|
||||||
[entrypointWithoutExtIndex]: {
|
[pageKey]: {
|
||||||
handler: `${getFileName(LAUNCHER_FILENAME).slice(0, -3)}.launcher`,
|
handler: `${getFileName(LAUNCHER_FILENAME).slice(0, -3)}.launcher`,
|
||||||
runtime: nodeVersion.runtime,
|
runtime: nodeVersion.runtime,
|
||||||
},
|
},
|
||||||
|
|||||||
58
packages/plugin-node/test/build.test.ts
vendored
58
packages/plugin-node/test/build.test.ts
vendored
@@ -1,6 +1,7 @@
|
|||||||
import { join } from 'path';
|
import path from 'path';
|
||||||
import { parse } from 'url';
|
import { parse } from 'url';
|
||||||
import { promises as fsp } from 'fs';
|
import { promises as fsp } from 'fs';
|
||||||
|
import { ZipFile } from 'yazl';
|
||||||
import { createFunction, Lambda } from '@vercel/fun';
|
import { createFunction, Lambda } from '@vercel/fun';
|
||||||
import {
|
import {
|
||||||
Request,
|
Request,
|
||||||
@@ -11,6 +12,7 @@ import {
|
|||||||
Headers,
|
Headers,
|
||||||
} from 'node-fetch';
|
} from 'node-fetch';
|
||||||
import { build } from '../src';
|
import { build } from '../src';
|
||||||
|
import { runNpmInstall, streamToBuffer } from '@vercel/build-utils';
|
||||||
|
|
||||||
interface TestParams {
|
interface TestParams {
|
||||||
fixture: string;
|
fixture: string;
|
||||||
@@ -50,40 +52,64 @@ function withFixture<T>(
|
|||||||
t: (props: TestParams) => Promise<T>
|
t: (props: TestParams) => Promise<T>
|
||||||
): () => Promise<T> {
|
): () => Promise<T> {
|
||||||
return async () => {
|
return async () => {
|
||||||
const fixture = join(__dirname, 'fixtures', name);
|
const fixture = path.join(__dirname, 'fixtures', name);
|
||||||
const functions = new Map<string, Lambda>();
|
const functions = new Map<string, Lambda>();
|
||||||
|
|
||||||
async function fetch(r: RequestInfo, init?: RequestInit) {
|
async function fetch(r: RequestInfo, init?: RequestInit) {
|
||||||
const req = new Request(r, init);
|
const req = new Request(r, init);
|
||||||
const url = parse(req.url);
|
const url = parse(req.url);
|
||||||
const pathWithIndex = join(
|
const functionPath = url.pathname!.substring(1);
|
||||||
url.pathname!,
|
|
||||||
url.pathname!.endsWith('/index') ? '' : 'index'
|
|
||||||
).substring(1);
|
|
||||||
|
|
||||||
let status = 404;
|
let status = 404;
|
||||||
let headers: HeadersInit = {};
|
let headers: HeadersInit = {};
|
||||||
let body: string | Buffer = 'Function not found';
|
let body: string | Buffer = 'Function not found';
|
||||||
|
|
||||||
let fn = functions.get(pathWithIndex);
|
let fn = functions.get(functionPath);
|
||||||
if (!fn) {
|
if (!fn) {
|
||||||
const manifest = JSON.parse(
|
const manifest = JSON.parse(
|
||||||
await fsp.readFile(
|
await fsp.readFile(
|
||||||
join(fixture, '.output/functions-manifest.json'),
|
path.join(fixture, '.output/functions-manifest.json'),
|
||||||
'utf8'
|
'utf8'
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
const functionManifest = manifest.pages[pathWithIndex];
|
|
||||||
|
const keyFile = `${functionPath}.js`;
|
||||||
|
const keyIndex = `${functionPath}/index.js`;
|
||||||
|
const fnKey = keyFile in manifest.pages ? keyFile : keyIndex;
|
||||||
|
const functionManifest = manifest.pages[fnKey];
|
||||||
|
|
||||||
if (functionManifest) {
|
if (functionManifest) {
|
||||||
const dir = join(fixture, '.output/server/pages', pathWithIndex);
|
const entry = path.join(fixture, '.output/server/pages', fnKey);
|
||||||
|
const nftFile = JSON.parse(
|
||||||
|
await fsp.readFile(`${entry}.nft.json`, 'utf8')
|
||||||
|
);
|
||||||
|
|
||||||
|
const zip = new ZipFile();
|
||||||
|
zip.addFile(
|
||||||
|
path.join(fixture, '.output/server/pages', fnKey),
|
||||||
|
path.join('.output/server/pages', fnKey)
|
||||||
|
);
|
||||||
|
|
||||||
|
nftFile.files.forEach((f: { input: string; output: string }) => {
|
||||||
|
const input = path.join(path.dirname(entry), f.input);
|
||||||
|
zip.addFile(input, f.output);
|
||||||
|
});
|
||||||
|
zip.end();
|
||||||
|
|
||||||
|
const handler = path.posix.join(
|
||||||
|
'.output/server/pages',
|
||||||
|
path.dirname(fnKey),
|
||||||
|
functionManifest.handler
|
||||||
|
);
|
||||||
|
|
||||||
fn = await createFunction({
|
fn = await createFunction({
|
||||||
Code: {
|
Code: {
|
||||||
Directory: dir,
|
ZipFile: await streamToBuffer(zip.outputStream),
|
||||||
},
|
},
|
||||||
Handler: functionManifest.handler,
|
Handler: handler,
|
||||||
Runtime: functionManifest.runtime,
|
Runtime: functionManifest.runtime,
|
||||||
});
|
});
|
||||||
functions.set(pathWithIndex, fn);
|
functions.set(functionPath, fn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,6 +135,12 @@ function withFixture<T>(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
await fsp.lstat(path.join(fixture, 'package.json')).catch(() => false)
|
||||||
|
) {
|
||||||
|
await runNpmInstall(fixture);
|
||||||
|
}
|
||||||
|
|
||||||
await build({ workPath: fixture });
|
await build({ workPath: fixture });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"private": false,
|
"private": false,
|
||||||
"name": "vercel-plugin-python",
|
"name": "vercel-plugin-python",
|
||||||
"version": "1.0.0-canary.2",
|
"version": "1.0.0-canary.3",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"files": [
|
"files": [
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vercel/build-utils": "2.12.3-canary.18",
|
"@vercel/build-utils": "2.12.3-canary.18",
|
||||||
"@vercel/python": "2.0.6-canary.5"
|
"@vercel/python": "2.0.6-canary.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vercel/python",
|
"name": "@vercel/python",
|
||||||
"version": "2.0.6-canary.5",
|
"version": "2.0.6-canary.6",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/python",
|
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/python",
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
createLambda,
|
createLambda,
|
||||||
shouldServe,
|
shouldServe,
|
||||||
debug,
|
debug,
|
||||||
|
NowBuildError,
|
||||||
} from '@vercel/build-utils';
|
} from '@vercel/build-utils';
|
||||||
import { installRequirement, installRequirementsFile } from './install';
|
import { installRequirement, installRequirementsFile } from './install';
|
||||||
|
|
||||||
@@ -22,6 +23,7 @@ async function pipenvConvert(cmd: string, srcDir: string) {
|
|||||||
const out = await execa.stdout(cmd, [], {
|
const out = await execa.stdout(cmd, [], {
|
||||||
cwd: srcDir,
|
cwd: srcDir,
|
||||||
});
|
});
|
||||||
|
debug('Contents of requirements.txt is: ' + out);
|
||||||
fs.writeFileSync(join(srcDir, 'requirements.txt'), out);
|
fs.writeFileSync(join(srcDir, 'requirements.txt'), out);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('Failed to run "pipfile2req"');
|
console.log('Failed to run "pipfile2req"');
|
||||||
@@ -57,6 +59,10 @@ export const build = async ({
|
|||||||
meta = {},
|
meta = {},
|
||||||
config,
|
config,
|
||||||
}: BuildOptions) => {
|
}: BuildOptions) => {
|
||||||
|
let pipPath = meta.isDev ? 'pip3' : 'pip3.9';
|
||||||
|
let pythonPath = meta.isDev ? 'python3' : 'python3.9';
|
||||||
|
let pythonRuntime = meta.isDev ? 'python3' : 'python3.9';
|
||||||
|
|
||||||
workPath = await downloadFilesInWorkPath({
|
workPath = await downloadFilesInWorkPath({
|
||||||
workPath,
|
workPath,
|
||||||
files: originalFiles,
|
files: originalFiles,
|
||||||
@@ -85,6 +91,8 @@ export const build = async ({
|
|||||||
console.log('Installing required dependencies...');
|
console.log('Installing required dependencies...');
|
||||||
|
|
||||||
await installRequirement({
|
await installRequirement({
|
||||||
|
pythonPath,
|
||||||
|
pipPath,
|
||||||
dependency: 'werkzeug',
|
dependency: 'werkzeug',
|
||||||
version: '1.0.1',
|
version: '1.0.1',
|
||||||
workPath,
|
workPath,
|
||||||
@@ -103,12 +111,29 @@ export const build = async ({
|
|||||||
if (pipfileLockDir) {
|
if (pipfileLockDir) {
|
||||||
debug('Found "Pipfile.lock"');
|
debug('Found "Pipfile.lock"');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const json = await readFile(join(pipfileLockDir, 'Pipfile.lock'), 'utf8');
|
||||||
|
const obj = JSON.parse(json);
|
||||||
|
if (!meta.isDev && obj?._meta?.requires?.python_version === '3.6') {
|
||||||
|
pipPath = 'pip3.6';
|
||||||
|
pythonPath = 'python3.6';
|
||||||
|
pythonRuntime = 'python3.6';
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
throw new NowBuildError({
|
||||||
|
code: 'INVALID_PIPFILE_LOCK',
|
||||||
|
message: 'Unable to parse Pipfile.lock',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Convert Pipenv.Lock to requirements.txt.
|
// Convert Pipenv.Lock to requirements.txt.
|
||||||
// We use a different`workPath` here because we want `pipfile-requirements` and it's dependencies
|
// We use a different`workPath` here because we want `pipfile-requirements` and it's dependencies
|
||||||
// to not be part of the lambda environment. By using pip's `--target` directive we can isolate
|
// to not be part of the lambda environment. By using pip's `--target` directive we can isolate
|
||||||
// it into a separate folder.
|
// it into a separate folder.
|
||||||
const tempDir = await getWriteableDirectory();
|
const tempDir = await getWriteableDirectory();
|
||||||
await installRequirement({
|
await installRequirement({
|
||||||
|
pythonPath,
|
||||||
|
pipPath,
|
||||||
dependency: 'pipfile-requirements',
|
dependency: 'pipfile-requirements',
|
||||||
version: '0.3.0',
|
version: '0.3.0',
|
||||||
workPath: tempDir,
|
workPath: tempDir,
|
||||||
@@ -130,6 +155,8 @@ export const build = async ({
|
|||||||
debug('Found local "requirements.txt"');
|
debug('Found local "requirements.txt"');
|
||||||
const requirementsTxtPath = fsFiles[requirementsTxt].fsPath;
|
const requirementsTxtPath = fsFiles[requirementsTxt].fsPath;
|
||||||
await installRequirementsFile({
|
await installRequirementsFile({
|
||||||
|
pythonPath,
|
||||||
|
pipPath,
|
||||||
filePath: requirementsTxtPath,
|
filePath: requirementsTxtPath,
|
||||||
workPath,
|
workPath,
|
||||||
meta,
|
meta,
|
||||||
@@ -138,6 +165,8 @@ export const build = async ({
|
|||||||
debug('Found global "requirements.txt"');
|
debug('Found global "requirements.txt"');
|
||||||
const requirementsTxtPath = fsFiles['requirements.txt'].fsPath;
|
const requirementsTxtPath = fsFiles['requirements.txt'].fsPath;
|
||||||
await installRequirementsFile({
|
await installRequirementsFile({
|
||||||
|
pythonPath,
|
||||||
|
pipPath,
|
||||||
filePath: requirementsTxtPath,
|
filePath: requirementsTxtPath,
|
||||||
workPath,
|
workPath,
|
||||||
meta,
|
meta,
|
||||||
@@ -163,7 +192,7 @@ export const build = async ({
|
|||||||
await writeFile(join(workPath, `${handlerPyFilename}.py`), handlerPyContents);
|
await writeFile(join(workPath, `${handlerPyFilename}.py`), handlerPyContents);
|
||||||
|
|
||||||
// Use the system-installed version of `python3` when running via `vercel dev`
|
// Use the system-installed version of `python3` when running via `vercel dev`
|
||||||
const runtime = meta.isDev ? 'python3' : 'python3.6';
|
const runtime = meta.isDev ? 'python3' : pythonRuntime;
|
||||||
|
|
||||||
const globOptions: GlobOptions = {
|
const globOptions: GlobOptions = {
|
||||||
cwd: workPath,
|
cwd: workPath,
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import execa from 'execa';
|
import execa from 'execa';
|
||||||
import { Meta, debug } from '@vercel/build-utils';
|
import { Meta, debug } from '@vercel/build-utils';
|
||||||
const pipPath = 'pip3';
|
|
||||||
|
|
||||||
const makeDependencyCheckCode = (dependency: string) => `
|
const makeDependencyCheckCode = (dependency: string) => `
|
||||||
from importlib import util
|
from importlib import util
|
||||||
@@ -9,10 +8,14 @@ spec = util.find_spec(dep)
|
|||||||
print(spec.origin)
|
print(spec.origin)
|
||||||
`;
|
`;
|
||||||
|
|
||||||
async function isInstalled(dependency: string, cwd: string) {
|
async function isInstalled(
|
||||||
|
pythonPath: string,
|
||||||
|
dependency: string,
|
||||||
|
cwd: string
|
||||||
|
) {
|
||||||
try {
|
try {
|
||||||
const { stdout } = await execa(
|
const { stdout } = await execa(
|
||||||
'python3',
|
pythonPath,
|
||||||
['-c', makeDependencyCheckCode(dependency)],
|
['-c', makeDependencyCheckCode(dependency)],
|
||||||
{
|
{
|
||||||
stdio: 'pipe',
|
stdio: 'pipe',
|
||||||
@@ -33,10 +36,14 @@ dependencies = distutils.text_file.TextFile(filename='${requirementsPath}').read
|
|||||||
pkg_resources.require(dependencies)
|
pkg_resources.require(dependencies)
|
||||||
`;
|
`;
|
||||||
|
|
||||||
async function areRequirementsInstalled(requirementsPath: string, cwd: string) {
|
async function areRequirementsInstalled(
|
||||||
|
pythonPath: string,
|
||||||
|
requirementsPath: string,
|
||||||
|
cwd: string
|
||||||
|
) {
|
||||||
try {
|
try {
|
||||||
await execa(
|
await execa(
|
||||||
'python3',
|
pythonPath,
|
||||||
['-c', makeRequirementsCheckCode(requirementsPath)],
|
['-c', makeRequirementsCheckCode(requirementsPath)],
|
||||||
{
|
{
|
||||||
stdio: 'pipe',
|
stdio: 'pipe',
|
||||||
@@ -49,7 +56,7 @@ async function areRequirementsInstalled(requirementsPath: string, cwd: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function pipInstall(workPath: string, args: string[]) {
|
async function pipInstall(pipPath: string, workPath: string, args: string[]) {
|
||||||
const target = '.';
|
const target = '.';
|
||||||
// See: https://github.com/pypa/pip/issues/4222#issuecomment-417646535
|
// See: https://github.com/pypa/pip/issues/4222#issuecomment-417646535
|
||||||
//
|
//
|
||||||
@@ -66,19 +73,21 @@ async function pipInstall(workPath: string, args: string[]) {
|
|||||||
target,
|
target,
|
||||||
...args,
|
...args,
|
||||||
];
|
];
|
||||||
debug(`Running "pip3 ${cmdArgs.join(' ')}"...`);
|
const pretty = `${pipPath} ${cmdArgs.join(' ')}`;
|
||||||
|
debug(`Running "${pretty}"...`);
|
||||||
try {
|
try {
|
||||||
await execa(pipPath, cmdArgs, {
|
await execa(pipPath, cmdArgs, {
|
||||||
cwd: workPath,
|
cwd: workPath,
|
||||||
stdio: 'pipe',
|
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(`Failed to run "pip3 ${cmdArgs.join(' ')}"`);
|
console.log(`Failed to run "${pretty}"`);
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface InstallRequirementArg {
|
interface InstallRequirementArg {
|
||||||
|
pythonPath: string;
|
||||||
|
pipPath: string;
|
||||||
dependency: string;
|
dependency: string;
|
||||||
version: string;
|
version: string;
|
||||||
workPath: string;
|
workPath: string;
|
||||||
@@ -91,23 +100,27 @@ interface InstallRequirementArg {
|
|||||||
// so vc_init should do runtime version checks to be compatible with any recent
|
// so vc_init should do runtime version checks to be compatible with any recent
|
||||||
// version of its dependencies
|
// version of its dependencies
|
||||||
export async function installRequirement({
|
export async function installRequirement({
|
||||||
|
pythonPath,
|
||||||
|
pipPath,
|
||||||
dependency,
|
dependency,
|
||||||
version,
|
version,
|
||||||
workPath,
|
workPath,
|
||||||
meta,
|
meta,
|
||||||
args = [],
|
args = [],
|
||||||
}: InstallRequirementArg) {
|
}: InstallRequirementArg) {
|
||||||
if (meta.isDev && (await isInstalled(dependency, workPath))) {
|
if (meta.isDev && (await isInstalled(pythonPath, dependency, workPath))) {
|
||||||
debug(
|
debug(
|
||||||
`Skipping ${dependency} dependency installation, already installed in ${workPath}`
|
`Skipping ${dependency} dependency installation, already installed in ${workPath}`
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const exact = `${dependency}==${version}`;
|
const exact = `${dependency}==${version}`;
|
||||||
await pipInstall(workPath, [exact, ...args]);
|
await pipInstall(pipPath, workPath, [exact, ...args]);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface InstallRequirementsFileArg {
|
interface InstallRequirementsFileArg {
|
||||||
|
pythonPath: string;
|
||||||
|
pipPath: string;
|
||||||
filePath: string;
|
filePath: string;
|
||||||
workPath: string;
|
workPath: string;
|
||||||
meta: Meta;
|
meta: Meta;
|
||||||
@@ -115,14 +128,19 @@ interface InstallRequirementsFileArg {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function installRequirementsFile({
|
export async function installRequirementsFile({
|
||||||
|
pythonPath,
|
||||||
|
pipPath,
|
||||||
filePath,
|
filePath,
|
||||||
workPath,
|
workPath,
|
||||||
meta,
|
meta,
|
||||||
args = [],
|
args = [],
|
||||||
}: InstallRequirementsFileArg) {
|
}: InstallRequirementsFileArg) {
|
||||||
if (meta.isDev && (await areRequirementsInstalled(filePath, workPath))) {
|
if (
|
||||||
|
meta.isDev &&
|
||||||
|
(await areRequirementsInstalled(pythonPath, filePath, workPath))
|
||||||
|
) {
|
||||||
debug(`Skipping requirements file installation, already installed`);
|
debug(`Skipping requirements file installation, already installed`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await pipInstall(workPath, ['--upgrade', '-r', filePath, ...args]);
|
await pipInstall(pipPath, workPath, ['--upgrade', '-r', filePath, ...args]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,6 @@
|
|||||||
"outDir": "dist",
|
"outDir": "dist",
|
||||||
"types": ["node"],
|
"types": ["node"],
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"target": "esnext"
|
"target": "es2018"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,12 @@ async function nowDeploy(bodies, randomness, uploadNowJson) {
|
|||||||
(path.extname(n) === '.sh' ? 0o100755 : 0o100644),
|
(path.extname(n) === '.sh' ? 0o100755 : 0o100644),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const { FORCE_BUILD_IN_REGION, NOW_DEBUG, VERCEL_DEBUG } = process.env;
|
const {
|
||||||
|
FORCE_BUILD_IN_REGION,
|
||||||
|
NOW_DEBUG,
|
||||||
|
VERCEL_DEBUG,
|
||||||
|
VERCEL_BUILDER_DEBUG,
|
||||||
|
} = process.env;
|
||||||
const nowJson = JSON.parse(bodies['vercel.json'] || bodies['now.json']);
|
const nowJson = JSON.parse(bodies['vercel.json'] || bodies['now.json']);
|
||||||
|
|
||||||
const nowDeployPayload = {
|
const nowDeployPayload = {
|
||||||
@@ -34,6 +39,7 @@ async function nowDeploy(bodies, randomness, uploadNowJson) {
|
|||||||
FORCE_BUILD_IN_REGION,
|
FORCE_BUILD_IN_REGION,
|
||||||
NOW_DEBUG,
|
NOW_DEBUG,
|
||||||
VERCEL_DEBUG,
|
VERCEL_DEBUG,
|
||||||
|
VERCEL_BUILDER_DEBUG,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
name: 'test2020',
|
name: 'test2020',
|
||||||
|
|||||||
35
yarn.lock
35
yarn.lock
@@ -2537,7 +2537,7 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/yargs-parser" "*"
|
"@types/yargs-parser" "*"
|
||||||
|
|
||||||
"@types/yazl@^2.4.1":
|
"@types/yazl@2.4.2", "@types/yazl@^2.4.1":
|
||||||
version "2.4.2"
|
version "2.4.2"
|
||||||
resolved "https://registry.yarnpkg.com/@types/yazl/-/yazl-2.4.2.tgz#d5f8a4752261badbf1a36e8b49e042dc18ec84bc"
|
resolved "https://registry.yarnpkg.com/@types/yazl/-/yazl-2.4.2.tgz#d5f8a4752261badbf1a36e8b49e042dc18ec84bc"
|
||||||
integrity sha512-T+9JH8O2guEjXNxqmybzQ92mJUh2oCwDDMSSimZSe1P+pceZiFROZLYmcbqkzV5EUwz6VwcKXCO2S2yUpra6XQ==
|
integrity sha512-T+9JH8O2guEjXNxqmybzQ92mJUh2oCwDDMSSimZSe1P+pceZiFROZLYmcbqkzV5EUwz6VwcKXCO2S2yUpra6XQ==
|
||||||
@@ -2613,10 +2613,10 @@
|
|||||||
"@typescript-eslint/types" "4.28.0"
|
"@typescript-eslint/types" "4.28.0"
|
||||||
eslint-visitor-keys "^2.0.0"
|
eslint-visitor-keys "^2.0.0"
|
||||||
|
|
||||||
"@vercel/fun@1.0.1":
|
"@vercel/fun@1.0.3":
|
||||||
version "1.0.1"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/@vercel/fun/-/fun-1.0.1.tgz#4b25f50c27bbbdca5b7e71f25ef5018701467962"
|
resolved "https://registry.yarnpkg.com/@vercel/fun/-/fun-1.0.3.tgz#5e5c4921a3a3a35ee129ce063e6b3f5c4b1eae48"
|
||||||
integrity sha512-v++KDTdKIr0357sm0AbyYk5jLmsqfUlOKhTsaN0Z/XHpk2YJHOLlGFTDzZPt+8i+ofdPG8lCfht729DHNQskJg==
|
integrity sha512-9LKNosC+Sdx7ZNU7dnj7XXasHzgRkuDnz9+N6JzSVvb0v8KOZN0ky4+wn3c1B0/wF3dTn2W5nEzgmnsrVCDtkQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@tootallnate/once" "2.0.0"
|
"@tootallnate/once" "2.0.0"
|
||||||
async-listen "1.0.0"
|
async-listen "1.0.0"
|
||||||
@@ -2629,6 +2629,7 @@
|
|||||||
node-fetch "2.6.1"
|
node-fetch "2.6.1"
|
||||||
path-match "1.2.4"
|
path-match "1.2.4"
|
||||||
promisepipe "3.0.0"
|
promisepipe "3.0.0"
|
||||||
|
semver "7.3.5"
|
||||||
stat-mode "0.3.0"
|
stat-mode "0.3.0"
|
||||||
stream-to-promise "2.2.0"
|
stream-to-promise "2.2.0"
|
||||||
tar "4.4.18"
|
tar "4.4.18"
|
||||||
@@ -8992,10 +8993,10 @@ node-fetch-npm@^2.0.2:
|
|||||||
json-parse-better-errors "^1.0.0"
|
json-parse-better-errors "^1.0.0"
|
||||||
safe-buffer "^5.1.1"
|
safe-buffer "^5.1.1"
|
||||||
|
|
||||||
node-fetch@2, node-fetch@^2:
|
node-fetch@2:
|
||||||
version "2.6.5"
|
version "2.6.6"
|
||||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.5.tgz#42735537d7f080a7e5f78b6c549b7146be1742fd"
|
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.6.tgz#1751a7c01834e8e1697758732e9efb6eeadfaf89"
|
||||||
integrity sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==
|
integrity sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==
|
||||||
dependencies:
|
dependencies:
|
||||||
whatwg-url "^5.0.0"
|
whatwg-url "^5.0.0"
|
||||||
|
|
||||||
@@ -9009,6 +9010,13 @@ node-fetch@2.6.1, node-fetch@^2.2.1, node-fetch@^2.3.0, node-fetch@^2.5.0, node-
|
|||||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
|
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
|
||||||
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
|
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
|
||||||
|
|
||||||
|
node-fetch@^2:
|
||||||
|
version "2.6.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.5.tgz#42735537d7f080a7e5f78b6c549b7146be1742fd"
|
||||||
|
integrity sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==
|
||||||
|
dependencies:
|
||||||
|
whatwg-url "^5.0.0"
|
||||||
|
|
||||||
node-gyp-build@^4.2.2:
|
node-gyp-build@^4.2.2:
|
||||||
version "4.2.3"
|
version "4.2.3"
|
||||||
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.3.tgz#ce6277f853835f718829efb47db20f3e4d9c4739"
|
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.3.tgz#ce6277f853835f718829efb47db20f3e4d9c4739"
|
||||||
@@ -10665,7 +10673,7 @@ semver@6.1.1:
|
|||||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.1.1.tgz#53f53da9b30b2103cd4f15eab3a18ecbcb210c9b"
|
resolved "https://registry.yarnpkg.com/semver/-/semver-6.1.1.tgz#53f53da9b30b2103cd4f15eab3a18ecbcb210c9b"
|
||||||
integrity sha512-rWYq2e5iYW+fFe/oPPtYJxYgjBm8sC4rmoGdUOgBB7VnwKt6HrL793l2voH1UlsyYZpJ4g0wfjnTEO1s1NP2eQ==
|
integrity sha512-rWYq2e5iYW+fFe/oPPtYJxYgjBm8sC4rmoGdUOgBB7VnwKt6HrL793l2voH1UlsyYZpJ4g0wfjnTEO1s1NP2eQ==
|
||||||
|
|
||||||
semver@7.x, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5:
|
semver@7.3.5, semver@7.x, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5:
|
||||||
version "7.3.5"
|
version "7.3.5"
|
||||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7"
|
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7"
|
||||||
integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==
|
integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==
|
||||||
@@ -12618,6 +12626,13 @@ yazl@2.4.3:
|
|||||||
dependencies:
|
dependencies:
|
||||||
buffer-crc32 "~0.2.3"
|
buffer-crc32 "~0.2.3"
|
||||||
|
|
||||||
|
yazl@2.5.1:
|
||||||
|
version "2.5.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/yazl/-/yazl-2.5.1.tgz#a3d65d3dd659a5b0937850e8609f22fffa2b5c35"
|
||||||
|
integrity sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==
|
||||||
|
dependencies:
|
||||||
|
buffer-crc32 "~0.2.3"
|
||||||
|
|
||||||
yn@3.1.1, yn@^3.0.0:
|
yn@3.1.1, yn@^3.0.0:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
|
resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
|
||||||
|
|||||||
Reference in New Issue
Block a user