mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-23 18:59:59 +00:00
Compare commits
12 Commits
vercel-plu
...
vercel-plu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
28f3bf9ef6 | ||
|
|
a936e92b8b | ||
|
|
ab1decf79d | ||
|
|
34408a7902 | ||
|
|
dc2d814d0f | ||
|
|
2402db92eb | ||
|
|
a1787c740d | ||
|
|
17fd88e044 | ||
|
|
03a8fbd3a7 | ||
|
|
8d37c1045f | ||
|
|
30c433d248 | ||
|
|
d89a79601c |
6
.github/CONTRIBUTING.md
vendored
6
.github/CONTRIBUTING.md
vendored
@@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
When contributing to this repository, please first discuss the change you wish to make via [GitHub Discussions](https://github.com/vercel/vercel/discussions/new) with the owners of this repository before submitting a Pull Request.
|
When contributing to this repository, please first discuss the change you wish to make via [GitHub Discussions](https://github.com/vercel/vercel/discussions/new) with the owners of this repository before submitting a Pull Request.
|
||||||
|
|
||||||
Please read our [code of conduct](CODE_OF_CONDUCT.md) and follow it in all your interactions with the project.
|
Please read our [Code of Conduct](CODE_OF_CONDUCT.md) and follow it in all your interactions with the project.
|
||||||
|
|
||||||
## Local development
|
## Local development
|
||||||
|
|
||||||
This project is configured in a monorepo pattern where one repo contains multiple npm packages. Dependencies are installed and managed with `yarn`, not `npm` CLI.
|
This project is configured in a monorepo, where one repository contains multiple npm packages. Dependencies are installed and managed with `yarn`, not `npm` CLI.
|
||||||
|
|
||||||
To get started, execute the following:
|
To get started, execute the following:
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ Make sure all the tests pass before making changes.
|
|||||||
|
|
||||||
## Verifying your change
|
## Verifying your change
|
||||||
|
|
||||||
Once you are done with your changes (we even suggest doing it along the way ), make sure all the test still run by running
|
Once you are done with your changes (we even suggest doing it along the way), make sure all the test still run by running:
|
||||||
|
|
||||||
```
|
```
|
||||||
yarn build && yarn test
|
yarn build && yarn test
|
||||||
|
|||||||
36
README.md
36
README.md
@@ -3,13 +3,21 @@
|
|||||||
<img src="https://assets.vercel.com/image/upload/v1588805858/repositories/vercel/logo.png" height="96">
|
<img src="https://assets.vercel.com/image/upload/v1588805858/repositories/vercel/logo.png" height="96">
|
||||||
<h3 align="center">Vercel</h3>
|
<h3 align="center">Vercel</h3>
|
||||||
</a>
|
</a>
|
||||||
<p align="center">Develop. Preview. Ship.</p>
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
[](https://github.com/vercel/vercel/actions/workflows/test-unit.yml)
|
<p align="center">
|
||||||
[](https://github.com/vercel/vercel/discussions)
|
Develop. Preview. Ship.
|
||||||
|
</p>
|
||||||
|
|
||||||
## Usage
|
<p align="center">
|
||||||
|
<a href="https://vercel.com/docs"><strong>Documentation</strong></a> ·
|
||||||
|
<a href="https://vercel.com/changelog"><strong>Changelog</strong></a> ·
|
||||||
|
<a href="https://vercel.com/templates"><strong>Templates</strong></a> ·
|
||||||
|
<a href="https://vercel.com/cli"><strong>CLI</strong></a>
|
||||||
|
</p>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
## Vercel
|
||||||
|
|
||||||
Vercel is a platform for **static sites and frontend frameworks**, built to integrate with your headless content, commerce, or database.
|
Vercel is a platform for **static sites and frontend frameworks**, built to integrate with your headless content, commerce, or database.
|
||||||
|
|
||||||
@@ -17,22 +25,16 @@ We provide a **frictionless developer experience** to take care of the hard thin
|
|||||||
|
|
||||||
We make it easy for frontend teams to **develop, preview, and ship** delightful user experiences, where performance is the default.
|
We make it easy for frontend teams to **develop, preview, and ship** delightful user experiences, where performance is the default.
|
||||||
|
|
||||||
Get started by [Importing a Git Project](https://vercel.com/new) and use `git push` to deploy. Alternatively, you can [install Vercel CLI](https://vercel.com/cli).
|
## Deploy
|
||||||
|
|
||||||
|
Get started by [importing a project](https://vercel.com/new) or using the [Vercel CLI](https://vercel.com/cli). Then, `git push` to deploy.
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
For details on how to use Vercel, check out our [documentation](https://vercel.com/docs).
|
For details on how to use Vercel, check out our [documentation](https://vercel.com/docs).
|
||||||
|
|
||||||
## Caught a Bug?
|
## Contributing
|
||||||
|
|
||||||
1. [Fork](https://help.github.com/articles/fork-a-repo/) this repository to your own GitHub account and then [clone](https://help.github.com/articles/cloning-a-repository/) it to your local device
|
- [Code of Conduct](https://github.com/vercel/vercel/blob/main/.github/CODE_OF_CONDUCT.md)
|
||||||
2. Install dependencies with `yarn install`
|
- [Contributing Guidelines](https://github.com/vercel/vercel/blob/main/.github/CONTRIBUTING.md)
|
||||||
3. Compile the code: `yarn build`
|
- [MIT License](https://github.com/vercel/vercel/blob/main/LICENSE)
|
||||||
4. Link the package to the global module directory: `cd ./packages/cli && yarn link`
|
|
||||||
5. You can start using `vercel` anywhere inside the command line
|
|
||||||
|
|
||||||
As always, you should use `yarn test-unit` to run the tests and see if your changes have broken anything.
|
|
||||||
|
|
||||||
## How to Create a Release
|
|
||||||
|
|
||||||
If you have write access to this repository, you can read more about how to publish a release [here](https://github.com/vercel/vercel/wiki/Creating-a-Release).
|
|
||||||
|
|||||||
50
examples/README.md
vendored
50
examples/README.md
vendored
@@ -1,28 +1,6 @@
|
|||||||
# Vercel Examples
|
# Vercel Examples
|
||||||
|
|
||||||
This is the public list of examples for **Vercel**.
|
To get started using any of these examples as your own project, [install Vercel](https://vercel.com/cli) and use either of the following commands in your terminal:
|
||||||
|
|
||||||
All of these ready to deploy examples feature a frontend framework or static site, created with zero configuration using the CLI tools they provide.
|
|
||||||
|
|
||||||
The `+functions` examples feature an `/api` directory as well, highlighting how to use serverless functions on top of a framework, again with zero configuration required.
|
|
||||||
|
|
||||||
## What is Vercel?
|
|
||||||
|
|
||||||
Vercel is a cloud platform for static frontends and serverless functions. It enables developers to host websites and web applications that deploy instantly, scale automatically, and require no supervision.
|
|
||||||
|
|
||||||
## What Does this Repository Contain?
|
|
||||||
|
|
||||||
This repository consists of multiple examples, created for use with the [Vercel](https://vercel.com) platform. In addition to this, it also contains:
|
|
||||||
|
|
||||||
- [Code of Conduct](https://github.com/vercel/vercel/blob/main/.github/CODE_OF_CONDUCT.md) - our Code of Conduct, adapted from the [Contributor Covenant](http://contributor-covenant.org)
|
|
||||||
- [Contributing Guidelines](https://github.com/vercel/vercel/blob/main/.github/CONTRIBUTING.md) - a guide on how to contribute to the examples repository
|
|
||||||
- [License](https://github.com/vercel/vercel/blob/main/LICENSE) - the standard MIT license under which these examples are published
|
|
||||||
|
|
||||||
We recommend familiarizing yourself with the above sections, particularly if you are looking to make a contribution.
|
|
||||||
|
|
||||||
## Deploying Examples
|
|
||||||
|
|
||||||
To get started using any of these examples as your own project, [install Vercel](https://vercel.com/download) and use either of the following commands in your terminal:
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
vercel init # Pick an example in the CLI
|
vercel init # Pick an example in the CLI
|
||||||
@@ -30,7 +8,7 @@ vercel init <example> # Create a new project from a specific <example>
|
|||||||
vercel init <example> <name> # Create a new project from a specific <example> with a different folder <name>
|
vercel init <example> <name> # Create a new project from a specific <example> with a different folder <name>
|
||||||
```
|
```
|
||||||
|
|
||||||
Deploying your project takes seconds and can be done with **just a single command**:
|
Deploying your project can be done with **a single command**:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
vercel # Deploy your project with the CLI
|
vercel # Deploy your project with the CLI
|
||||||
@@ -38,26 +16,6 @@ vercel # Deploy your project with the CLI
|
|||||||
|
|
||||||
With the `vercel` command, your project will be built and served by Vercel, providing you with a URL that can be shared immediately.
|
With the `vercel` command, your project will be built and served by Vercel, providing you with a URL that can be shared immediately.
|
||||||
|
|
||||||
## New Examples
|
|
||||||
|
|
||||||
We are continuously improving our examples based on best practices and feedback from the community. As a result, it is possible that example names will change and on occasion deprecated in favor of an improved implementation.
|
|
||||||
|
|
||||||
For example, the previous `nodejs` example showed a static frontend with a Node.js API. This is illustrated in the `svelte` example. Below is a table that lists some of the most popular previous examples and the equivalent replacement:
|
|
||||||
|
|
||||||
| Previous Example | New Example |
|
|
||||||
| ----------------- | ---------------------------------------------------------------------------------------- |
|
|
||||||
| **monorepo** | [gatsby-functions](https://github.com/vercel/vercel/tree/main/examples/gatsby) |
|
|
||||||
| **nodejs** | [svelte-functions](https://github.com/vercel/vercel/tree/main/examples/svelte) |
|
|
||||||
| **nextjs-static** | [nextjs](https://github.com/vercel/vercel/tree/main/examples/nextjs) |
|
|
||||||
| **vanilla-go** | [create-react-app](https://github.com/vercel/vercel/tree/main/examples/create-react-app) |
|
|
||||||
| **typescript** | [gatsby-functions](https://github.com/vercel/vercel/tree/main/examples/gatsby) |
|
|
||||||
|
|
||||||
## Migrating and Upgrading
|
|
||||||
|
|
||||||
If you have an existing project you would like to deploy with Vercel, we recommend reading our guide on [migrating to Vercel and zero configuration](https://vercel.com/guides/migrate-to-vercel). By combining the guide with this repository, you will quickly be able to understand how to deploy your application.
|
|
||||||
|
|
||||||
If you would like to upgrade a project to take advantage of zero configuration, you may find the [upgrade guide](https://vercel.com/guides/upgrade-to-zero-configuration) useful. The upgrade guide covers how to remove configuration from existing projects along with how to use the `/api` directory.
|
|
||||||
|
|
||||||
## How to Contribute
|
## How to Contribute
|
||||||
|
|
||||||
Contributing examples should be an enjoyable experience, as such we have created a set of [contributing guidelines](https://github.com/vercel/vercel/blob/main/.github/CONTRIBUTING.md) to help you do so.
|
Contributing examples should be an enjoyable experience, as such we have created a set of [contributing guidelines](https://github.com/vercel/vercel/blob/main/.github/CONTRIBUTING.md) to help you do so.
|
||||||
@@ -74,10 +32,6 @@ An issue can be raised by clicking the 'Issues' tab at the top of the repository
|
|||||||
|
|
||||||
When submitting an issue, please thoroughly and concisely describe the problem you are experiencing so that we may easily understand and resolve the issue in a timely manner.
|
When submitting an issue, please thoroughly and concisely describe the problem you are experiencing so that we may easily understand and resolve the issue in a timely manner.
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
This repository is an open source project. See the [License](https://github.com/vercel/vercel/blob/main/LICENSE).
|
|
||||||
|
|
||||||
## Get In Touch
|
## Get In Touch
|
||||||
|
|
||||||
If you have any questions that are not covered by raising an issue then please get in touch with us on [GitHub Discussions](https://github.com/vercel/vercel/discussions). There you will find both members of the community and staff who are happy to help answer questions on anything Vercel related.
|
If you have any questions that are not covered by raising an issue then please get in touch with us on [GitHub Discussions](https://github.com/vercel/vercel/discussions). There you will find both members of the community and staff who are happy to help answer questions on anything Vercel related.
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export default function Home() {
|
|||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href="https://github.com/vercel/next.js/tree/master/examples"
|
href="https://github.com/vercel/next.js/tree/canary/examples"
|
||||||
className={styles.card}
|
className={styles.card}
|
||||||
>
|
>
|
||||||
<h2>Examples →</h2>
|
<h2>Examples →</h2>
|
||||||
|
|||||||
@@ -1,15 +1,10 @@
|
|||||||
.container {
|
.container {
|
||||||
min-height: 100vh;
|
padding: 0 2rem;
|
||||||
padding: 0 0.5rem;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
height: 100vh;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.main {
|
.main {
|
||||||
padding: 5rem 0;
|
min-height: 100vh;
|
||||||
|
padding: 4rem 0;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -18,10 +13,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
width: 100%;
|
|
||||||
height: 100px;
|
|
||||||
border-top: 1px solid #eaeaea;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
padding: 2rem 0;
|
||||||
|
border-top: 1px solid #eaeaea;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
@@ -56,6 +51,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.description {
|
.description {
|
||||||
|
margin: 4rem 0;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
}
|
}
|
||||||
@@ -75,7 +71,6 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
margin-top: 3rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
@@ -87,7 +82,7 @@
|
|||||||
border: 1px solid #eaeaea;
|
border: 1px solid #eaeaea;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
transition: color 0.15s ease, border-color 0.15s ease;
|
transition: color 0.15s ease, border-color 0.15s ease;
|
||||||
width: 45%;
|
max-width: 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card:hover,
|
.card:hover,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vercel/build-utils",
|
"name": "@vercel/build-utils",
|
||||||
"version": "2.13.1-canary.0",
|
"version": "2.13.1-canary.2",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"types": "./dist/index.d.js",
|
"types": "./dist/index.d.js",
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
"@types/node-fetch": "^2.1.6",
|
"@types/node-fetch": "^2.1.6",
|
||||||
"@types/semver": "6.0.0",
|
"@types/semver": "6.0.0",
|
||||||
"@types/yazl": "^2.4.1",
|
"@types/yazl": "^2.4.1",
|
||||||
"@vercel/frameworks": "0.5.1-canary.20",
|
"@vercel/frameworks": "0.5.1-canary.21",
|
||||||
"@vercel/ncc": "0.24.0",
|
"@vercel/ncc": "0.24.0",
|
||||||
"aggregate-error": "3.0.1",
|
"aggregate-error": "3.0.1",
|
||||||
"async-retry": "1.2.3",
|
"async-retry": "1.2.3",
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import fs from 'fs-extra';
|
|||||||
import { join, parse, relative, dirname, basename, extname } from 'path';
|
import { join, parse, relative, dirname, basename, extname } from 'path';
|
||||||
import glob from './fs/glob';
|
import glob from './fs/glob';
|
||||||
import { normalizePath } from './fs/normalize-path';
|
import { normalizePath } from './fs/normalize-path';
|
||||||
import { FILES_SYMBOL, Lambda } from './lambda';
|
import { Lambda } from './lambda';
|
||||||
import type { BuildOptions, Files } from './types';
|
import type { BuildOptions } from './types';
|
||||||
import { debug, getIgnoreFilter } from '.';
|
import { debug, getIgnoreFilter } from '.';
|
||||||
|
|
||||||
// `.output` was already created by the Build Command, so we have
|
// `.output` was already created by the Build Command, so we have
|
||||||
@@ -116,8 +116,7 @@ export function _experimental_convertRuntimeToPlugin(
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// @ts-ignore This symbol is a private API
|
const lambdaFiles = output.files;
|
||||||
const lambdaFiles: Files = output[FILES_SYMBOL];
|
|
||||||
|
|
||||||
// When deploying, the `files` that are passed to the Legacy Runtimes already
|
// When deploying, the `files` that are passed to the Legacy Runtimes already
|
||||||
// have certain files that are ignored stripped, but locally, that list of
|
// have certain files that are ignored stripped, but locally, that list of
|
||||||
|
|||||||
@@ -328,8 +328,12 @@ export async function runNpmInstall(
|
|||||||
const opts: SpawnOptionsExtended = { cwd: destPath, ...spawnOpts };
|
const opts: SpawnOptionsExtended = { cwd: destPath, ...spawnOpts };
|
||||||
const env = opts.env ? { ...opts.env } : { ...process.env };
|
const env = opts.env ? { ...opts.env } : { ...process.env };
|
||||||
delete env.NODE_ENV;
|
delete env.NODE_ENV;
|
||||||
opts.env = env;
|
opts.env = getEnvForPackageManager({
|
||||||
|
cliType,
|
||||||
|
lockfileVersion,
|
||||||
|
nodeVersion,
|
||||||
|
env,
|
||||||
|
});
|
||||||
let commandArgs: string[];
|
let commandArgs: string[];
|
||||||
|
|
||||||
if (cliType === 'npm') {
|
if (cliType === 'npm') {
|
||||||
@@ -337,25 +341,9 @@ export async function runNpmInstall(
|
|||||||
commandArgs = args
|
commandArgs = args
|
||||||
.filter(a => a !== '--prefer-offline')
|
.filter(a => a !== '--prefer-offline')
|
||||||
.concat(['install', '--no-audit', '--unsafe-perm']);
|
.concat(['install', '--no-audit', '--unsafe-perm']);
|
||||||
|
|
||||||
// If the lockfile version is 2 or greater and the node version is less than 16 than we will force npm7 to be used
|
|
||||||
if (
|
|
||||||
typeof lockfileVersion === 'number' &&
|
|
||||||
lockfileVersion >= 2 &&
|
|
||||||
(nodeVersion?.major || 0) < 16
|
|
||||||
) {
|
|
||||||
// Ensure that npm 7 is at the beginning of the `$PATH`
|
|
||||||
env.PATH = `/node16/bin-npm7:${env.PATH}`;
|
|
||||||
console.log('Detected `package-lock.json` generated by npm 7...');
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
opts.prettyCommand = 'yarn install';
|
opts.prettyCommand = 'yarn install';
|
||||||
commandArgs = ['install', ...args];
|
commandArgs = ['install', ...args];
|
||||||
|
|
||||||
// Yarn v2 PnP mode may be activated, so force "node-modules" linker style
|
|
||||||
if (!env.YARN_NODE_LINKER) {
|
|
||||||
env.YARN_NODE_LINKER = 'node-modules';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.env.NPM_ONLY_PRODUCTION) {
|
if (process.env.NPM_ONLY_PRODUCTION) {
|
||||||
@@ -365,6 +353,65 @@ export async function runNpmInstall(
|
|||||||
return spawnAsync(cliType, commandArgs, opts);
|
return spawnAsync(cliType, commandArgs, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getEnvForPackageManager({
|
||||||
|
cliType,
|
||||||
|
lockfileVersion,
|
||||||
|
nodeVersion,
|
||||||
|
env,
|
||||||
|
}: {
|
||||||
|
cliType: CliType;
|
||||||
|
lockfileVersion: number | undefined;
|
||||||
|
nodeVersion: NodeVersion | undefined;
|
||||||
|
env: { [x: string]: string | undefined };
|
||||||
|
}) {
|
||||||
|
const newEnv: { [x: string]: string | undefined } = { ...env };
|
||||||
|
if (cliType === 'npm') {
|
||||||
|
if (
|
||||||
|
typeof lockfileVersion === 'number' &&
|
||||||
|
lockfileVersion >= 2 &&
|
||||||
|
(nodeVersion?.major || 0) < 16
|
||||||
|
) {
|
||||||
|
// Ensure that npm 7 is at the beginning of the `$PATH`
|
||||||
|
newEnv.PATH = `/node16/bin-npm7:${env.PATH}`;
|
||||||
|
console.log('Detected `package-lock.json` generated by npm 7...');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Yarn v2 PnP mode may be activated, so force "node-modules" linker style
|
||||||
|
if (!env.YARN_NODE_LINKER) {
|
||||||
|
newEnv.YARN_NODE_LINKER = 'node-modules';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newEnv;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function runCustomInstallCommand({
|
||||||
|
destPath,
|
||||||
|
installCommand,
|
||||||
|
nodeVersion,
|
||||||
|
spawnOpts,
|
||||||
|
}: {
|
||||||
|
destPath: string;
|
||||||
|
installCommand: string;
|
||||||
|
nodeVersion: NodeVersion;
|
||||||
|
spawnOpts?: SpawnOptions;
|
||||||
|
}) {
|
||||||
|
console.log(`Running "install" command: \`${installCommand}\`...`);
|
||||||
|
const { cliType, lockfileVersion } = await scanParentDirs(destPath);
|
||||||
|
const env = getEnvForPackageManager({
|
||||||
|
cliType,
|
||||||
|
lockfileVersion,
|
||||||
|
nodeVersion,
|
||||||
|
env: spawnOpts?.env || {},
|
||||||
|
});
|
||||||
|
debug(`Running with $PATH:`, env?.PATH || '');
|
||||||
|
await execCommand(installCommand, {
|
||||||
|
...spawnOpts,
|
||||||
|
env,
|
||||||
|
cwd: destPath,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export async function runPackageJsonScript(
|
export async function runPackageJsonScript(
|
||||||
destPath: string,
|
destPath: string,
|
||||||
scriptNames: string | Iterable<string>,
|
scriptNames: string | Iterable<string>,
|
||||||
@@ -385,23 +432,24 @@ export async function runPackageJsonScript(
|
|||||||
debug('Running user script...');
|
debug('Running user script...');
|
||||||
const runScriptTime = Date.now();
|
const runScriptTime = Date.now();
|
||||||
|
|
||||||
const opts: SpawnOptionsExtended = { cwd: destPath, ...spawnOpts };
|
const opts: SpawnOptionsExtended = {
|
||||||
const env = (opts.env = { ...process.env, ...opts.env });
|
cwd: destPath,
|
||||||
|
...spawnOpts,
|
||||||
|
env: getEnvForPackageManager({
|
||||||
|
cliType,
|
||||||
|
lockfileVersion,
|
||||||
|
nodeVersion: undefined,
|
||||||
|
env: {
|
||||||
|
...process.env,
|
||||||
|
...spawnOpts?.env,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
if (cliType === 'npm') {
|
if (cliType === 'npm') {
|
||||||
opts.prettyCommand = `npm run ${scriptName}`;
|
opts.prettyCommand = `npm run ${scriptName}`;
|
||||||
|
|
||||||
if (typeof lockfileVersion === 'number' && lockfileVersion >= 2) {
|
|
||||||
// Ensure that npm 7 is at the beginning of the `$PATH`
|
|
||||||
env.PATH = `/node16/bin-npm7:${env.PATH}`;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
opts.prettyCommand = `yarn run ${scriptName}`;
|
opts.prettyCommand = `yarn run ${scriptName}`;
|
||||||
|
|
||||||
// Yarn v2 PnP mode may be activated, so force "node-modules" linker style
|
|
||||||
if (!env.YARN_NODE_LINKER) {
|
|
||||||
env.YARN_NODE_LINKER = 'node-modules';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`Running "${opts.prettyCommand}"`);
|
console.log(`Running "${opts.prettyCommand}"`);
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ import {
|
|||||||
runBundleInstall,
|
runBundleInstall,
|
||||||
runPipInstall,
|
runPipInstall,
|
||||||
runShellScript,
|
runShellScript,
|
||||||
|
runCustomInstallCommand,
|
||||||
|
getEnvForPackageManager,
|
||||||
getNodeVersion,
|
getNodeVersion,
|
||||||
getSpawnOptions,
|
getSpawnOptions,
|
||||||
getNodeBinPath,
|
getNodeBinPath,
|
||||||
@@ -61,6 +63,8 @@ export {
|
|||||||
runBundleInstall,
|
runBundleInstall,
|
||||||
runPipInstall,
|
runPipInstall,
|
||||||
runShellScript,
|
runShellScript,
|
||||||
|
runCustomInstallCommand,
|
||||||
|
getEnvForPackageManager,
|
||||||
getNodeVersion,
|
getNodeVersion,
|
||||||
getLatestNodeVersion,
|
getLatestNodeVersion,
|
||||||
getDiscontinuedNodeVersions,
|
getDiscontinuedNodeVersions,
|
||||||
|
|||||||
@@ -13,17 +13,6 @@ interface Environment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface LambdaOptions {
|
interface LambdaOptions {
|
||||||
zipBuffer: Buffer;
|
|
||||||
handler: string;
|
|
||||||
runtime: string;
|
|
||||||
memory?: number;
|
|
||||||
maxDuration?: number;
|
|
||||||
environment: Environment;
|
|
||||||
allowQuery?: string[];
|
|
||||||
regions?: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface CreateLambdaOptions {
|
|
||||||
files: Files;
|
files: Files;
|
||||||
handler: string;
|
handler: string;
|
||||||
runtime: string;
|
runtime: string;
|
||||||
@@ -32,6 +21,10 @@ interface CreateLambdaOptions {
|
|||||||
environment?: Environment;
|
environment?: Environment;
|
||||||
allowQuery?: string[];
|
allowQuery?: string[];
|
||||||
regions?: string[];
|
regions?: string[];
|
||||||
|
/**
|
||||||
|
* @deprecated Use `files` property instead.
|
||||||
|
*/
|
||||||
|
zipBuffer?: Buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface GetLambdaOptionsFromFunctionOptions {
|
interface GetLambdaOptionsFromFunctionOptions {
|
||||||
@@ -39,11 +32,9 @@ interface GetLambdaOptionsFromFunctionOptions {
|
|||||||
config?: Pick<Config, 'functions'>;
|
config?: Pick<Config, 'functions'>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FILES_SYMBOL = Symbol('files');
|
|
||||||
|
|
||||||
export class Lambda {
|
export class Lambda {
|
||||||
public type: 'Lambda';
|
public type: 'Lambda';
|
||||||
public zipBuffer: Buffer;
|
public files: Files;
|
||||||
public handler: string;
|
public handler: string;
|
||||||
public runtime: string;
|
public runtime: string;
|
||||||
public memory?: number;
|
public memory?: number;
|
||||||
@@ -51,19 +42,54 @@ export class Lambda {
|
|||||||
public environment: Environment;
|
public environment: Environment;
|
||||||
public allowQuery?: string[];
|
public allowQuery?: string[];
|
||||||
public regions?: string[];
|
public regions?: string[];
|
||||||
|
/**
|
||||||
|
* @deprecated Use `await lambda.createZip()` instead.
|
||||||
|
*/
|
||||||
|
public zipBuffer?: Buffer;
|
||||||
|
|
||||||
constructor({
|
constructor({
|
||||||
zipBuffer,
|
files,
|
||||||
handler,
|
handler,
|
||||||
runtime,
|
runtime,
|
||||||
maxDuration,
|
maxDuration,
|
||||||
memory,
|
memory,
|
||||||
environment,
|
environment = {},
|
||||||
allowQuery,
|
allowQuery,
|
||||||
regions,
|
regions,
|
||||||
|
zipBuffer,
|
||||||
}: LambdaOptions) {
|
}: LambdaOptions) {
|
||||||
|
if (!zipBuffer) {
|
||||||
|
assert(typeof files === 'object', '"files" must be an object');
|
||||||
|
}
|
||||||
|
assert(typeof handler === 'string', '"handler" is not a string');
|
||||||
|
assert(typeof runtime === 'string', '"runtime" is not a string');
|
||||||
|
assert(typeof environment === 'object', '"environment" is not an object');
|
||||||
|
|
||||||
|
if (memory !== undefined) {
|
||||||
|
assert(typeof memory === 'number', '"memory" is not a number');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxDuration !== undefined) {
|
||||||
|
assert(typeof maxDuration === 'number', '"maxDuration" is not a number');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allowQuery !== undefined) {
|
||||||
|
assert(Array.isArray(allowQuery), '"allowQuery" is not an Array');
|
||||||
|
assert(
|
||||||
|
allowQuery.every(q => typeof q === 'string'),
|
||||||
|
'"allowQuery" is not a string Array'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (regions !== undefined) {
|
||||||
|
assert(Array.isArray(regions), '"regions" is not an Array');
|
||||||
|
assert(
|
||||||
|
regions.every(r => typeof r === 'string'),
|
||||||
|
'"regions" is not a string Array'
|
||||||
|
);
|
||||||
|
}
|
||||||
this.type = 'Lambda';
|
this.type = 'Lambda';
|
||||||
this.zipBuffer = zipBuffer;
|
this.files = files;
|
||||||
this.handler = handler;
|
this.handler = handler;
|
||||||
this.runtime = runtime;
|
this.runtime = runtime;
|
||||||
this.memory = memory;
|
this.memory = memory;
|
||||||
@@ -71,70 +97,36 @@ export class Lambda {
|
|||||||
this.environment = environment;
|
this.environment = environment;
|
||||||
this.allowQuery = allowQuery;
|
this.allowQuery = allowQuery;
|
||||||
this.regions = regions;
|
this.regions = regions;
|
||||||
|
this.zipBuffer = zipBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
async createZip(): Promise<Buffer> {
|
||||||
|
let { zipBuffer } = this;
|
||||||
|
if (!zipBuffer) {
|
||||||
|
await sema.acquire();
|
||||||
|
try {
|
||||||
|
zipBuffer = await createZip(this.files);
|
||||||
|
} finally {
|
||||||
|
sema.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return zipBuffer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const sema = new Sema(10);
|
const sema = new Sema(10);
|
||||||
const mtime = new Date(1540000000000);
|
const mtime = new Date(1540000000000);
|
||||||
|
|
||||||
export async function createLambda({
|
/**
|
||||||
files,
|
* @deprecated Use `new Lambda()` instead.
|
||||||
handler,
|
*/
|
||||||
runtime,
|
export async function createLambda(opts: LambdaOptions): Promise<Lambda> {
|
||||||
memory,
|
const lambda = new Lambda(opts);
|
||||||
maxDuration,
|
|
||||||
environment = {},
|
|
||||||
allowQuery,
|
|
||||||
regions,
|
|
||||||
}: CreateLambdaOptions): Promise<Lambda> {
|
|
||||||
assert(typeof files === 'object', '"files" must be an object');
|
|
||||||
assert(typeof handler === 'string', '"handler" is not a string');
|
|
||||||
assert(typeof runtime === 'string', '"runtime" is not a string');
|
|
||||||
assert(typeof environment === 'object', '"environment" is not an object');
|
|
||||||
|
|
||||||
if (memory !== undefined) {
|
// backwards compat
|
||||||
assert(typeof memory === 'number', '"memory" is not a number');
|
lambda.zipBuffer = await lambda.createZip();
|
||||||
}
|
|
||||||
|
|
||||||
if (maxDuration !== undefined) {
|
return lambda;
|
||||||
assert(typeof maxDuration === 'number', '"maxDuration" is not a number');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (allowQuery !== undefined) {
|
|
||||||
assert(Array.isArray(allowQuery), '"allowQuery" is not an Array');
|
|
||||||
assert(
|
|
||||||
allowQuery.every(q => typeof q === 'string'),
|
|
||||||
'"allowQuery" is not a string Array'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (regions !== undefined) {
|
|
||||||
assert(Array.isArray(regions), '"regions" is not an Array');
|
|
||||||
assert(
|
|
||||||
regions.every(r => typeof r === 'string'),
|
|
||||||
'"regions" is not a string Array'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
await sema.acquire();
|
|
||||||
|
|
||||||
try {
|
|
||||||
const zipBuffer = await createZip(files);
|
|
||||||
const lambda = new Lambda({
|
|
||||||
zipBuffer,
|
|
||||||
handler,
|
|
||||||
runtime,
|
|
||||||
memory,
|
|
||||||
maxDuration,
|
|
||||||
environment,
|
|
||||||
regions,
|
|
||||||
});
|
|
||||||
// @ts-ignore This symbol is a private API
|
|
||||||
lambda[FILES_SYMBOL] = files;
|
|
||||||
return lambda;
|
|
||||||
} finally {
|
|
||||||
sema.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createZip(files: Files): Promise<Buffer> {
|
export async function createZip(files: Files): Promise<Buffer> {
|
||||||
@@ -177,7 +169,7 @@ export async function getLambdaOptionsFromFunction({
|
|||||||
}: GetLambdaOptionsFromFunctionOptions): Promise<
|
}: GetLambdaOptionsFromFunctionOptions): Promise<
|
||||||
Pick<LambdaOptions, 'memory' | 'maxDuration'>
|
Pick<LambdaOptions, 'memory' | 'maxDuration'>
|
||||||
> {
|
> {
|
||||||
if (config && config.functions) {
|
if (config?.functions) {
|
||||||
for (const [pattern, fn] of Object.entries(config.functions)) {
|
for (const [pattern, fn] of Object.entries(config.functions)) {
|
||||||
if (sourceFile === pattern || minimatch(sourceFile, pattern)) {
|
if (sourceFile === pattern || minimatch(sourceFile, pattern)) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
109
packages/build-utils/test/unit.get-env-for-package-manager.test.ts
vendored
Normal file
109
packages/build-utils/test/unit.get-env-for-package-manager.test.ts
vendored
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
import assert from 'assert';
|
||||||
|
import { getEnvForPackageManager, NodeVersion } from '../src';
|
||||||
|
import { CliType } from '../src/fs/run-user-scripts';
|
||||||
|
|
||||||
|
describe('Test `getEnvForPackageManager()`', () => {
|
||||||
|
const cases = [
|
||||||
|
{
|
||||||
|
name: 'should do nothing to env for npm < 6 and node < 16',
|
||||||
|
args: {
|
||||||
|
cliType: 'npm' as CliType,
|
||||||
|
nodeVersion: {
|
||||||
|
major: 14,
|
||||||
|
} as NodeVersion,
|
||||||
|
lockfileVersion: 1,
|
||||||
|
env: {
|
||||||
|
FOO: 'bar',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: {
|
||||||
|
FOO: 'bar',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'should set path if npm 7+ is detected and node < 16',
|
||||||
|
args: {
|
||||||
|
cliType: 'npm' as CliType,
|
||||||
|
nodeVersion: {
|
||||||
|
major: 14,
|
||||||
|
} as NodeVersion,
|
||||||
|
lockfileVersion: 2,
|
||||||
|
env: {
|
||||||
|
FOO: 'bar',
|
||||||
|
PATH: 'foo',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: {
|
||||||
|
FOO: 'bar',
|
||||||
|
PATH: `/node16/bin-npm7:foo`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'should not set path if node is 16 and npm 7+ is detected',
|
||||||
|
args: {
|
||||||
|
cliType: 'npm' as CliType,
|
||||||
|
nodeVersion: {
|
||||||
|
major: 16,
|
||||||
|
} as NodeVersion,
|
||||||
|
lockfileVersion: 2,
|
||||||
|
env: {
|
||||||
|
FOO: 'bar',
|
||||||
|
PATH: 'foo',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: {
|
||||||
|
FOO: 'bar',
|
||||||
|
PATH: 'foo',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'should set YARN_NODE_LINKER w/yarn if it is not already defined',
|
||||||
|
args: {
|
||||||
|
cliType: 'yarn' as CliType,
|
||||||
|
nodeVersion: {
|
||||||
|
major: 16,
|
||||||
|
} as NodeVersion,
|
||||||
|
lockfileVersion: 2,
|
||||||
|
env: {
|
||||||
|
FOO: 'bar',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: {
|
||||||
|
FOO: 'bar',
|
||||||
|
YARN_NODE_LINKER: 'node-modules',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'should not set YARN_NODE_LINKER if it already exists',
|
||||||
|
args: {
|
||||||
|
cliType: 'yarn' as CliType,
|
||||||
|
nodeVersion: {
|
||||||
|
major: 16,
|
||||||
|
} as NodeVersion,
|
||||||
|
lockfileVersion: 2,
|
||||||
|
env: {
|
||||||
|
FOO: 'bar',
|
||||||
|
YARN_NODE_LINKER: 'exists',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: {
|
||||||
|
FOO: 'bar',
|
||||||
|
YARN_NODE_LINKER: 'exists',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const { name, want, args } of cases) {
|
||||||
|
it(name, () => {
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
getEnvForPackageManager({
|
||||||
|
cliType: args.cliType,
|
||||||
|
lockfileVersion: args.lockfileVersion,
|
||||||
|
nodeVersion: args.nodeVersion,
|
||||||
|
env: args.env,
|
||||||
|
}),
|
||||||
|
want
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "vercel",
|
"name": "vercel",
|
||||||
"version": "23.1.3-canary.73",
|
"version": "23.1.3-canary.75",
|
||||||
"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",
|
||||||
@@ -43,14 +43,14 @@
|
|||||||
"node": ">= 12"
|
"node": ">= 12"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vercel/build-utils": "2.13.1-canary.0",
|
"@vercel/build-utils": "2.13.1-canary.2",
|
||||||
"@vercel/go": "1.2.4-canary.5",
|
"@vercel/go": "1.2.4-canary.7",
|
||||||
"@vercel/node": "1.12.2-canary.8",
|
"@vercel/node": "1.12.2-canary.10",
|
||||||
"@vercel/python": "2.1.2-canary.3",
|
"@vercel/python": "2.1.2-canary.5",
|
||||||
"@vercel/ruby": "1.2.10-canary.1",
|
"@vercel/ruby": "1.2.10-canary.3",
|
||||||
"update-notifier": "4.1.0",
|
"update-notifier": "4.1.0",
|
||||||
"vercel-plugin-middleware": "0.0.0-canary.25",
|
"vercel-plugin-middleware": "0.0.0-canary.27",
|
||||||
"vercel-plugin-node": "1.12.2-canary.40"
|
"vercel-plugin-node": "1.12.2-canary.42"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@next/env": "11.1.2",
|
"@next/env": "11.1.2",
|
||||||
@@ -90,8 +90,9 @@
|
|||||||
"@types/update-notifier": "5.1.0",
|
"@types/update-notifier": "5.1.0",
|
||||||
"@types/which": "1.3.2",
|
"@types/which": "1.3.2",
|
||||||
"@types/write-json-file": "2.2.1",
|
"@types/write-json-file": "2.2.1",
|
||||||
"@vercel/client": "10.2.3-canary.51",
|
"@vercel/client": "10.2.3-canary.53",
|
||||||
"@vercel/frameworks": "0.5.1-canary.20",
|
"@vercel/fetch-retry": "5.0.3",
|
||||||
|
"@vercel/frameworks": "0.5.1-canary.21",
|
||||||
"@vercel/ncc": "0.24.0",
|
"@vercel/ncc": "0.24.0",
|
||||||
"@vercel/nft": "0.17.0",
|
"@vercel/nft": "0.17.0",
|
||||||
"@zeit/fun": "0.11.2",
|
"@zeit/fun": "0.11.2",
|
||||||
@@ -142,7 +143,7 @@
|
|||||||
"ms": "2.1.2",
|
"ms": "2.1.2",
|
||||||
"node-fetch": "2.6.1",
|
"node-fetch": "2.6.1",
|
||||||
"npm-package-arg": "6.1.0",
|
"npm-package-arg": "6.1.0",
|
||||||
"open": "8.2.0",
|
"open": "8.4.0",
|
||||||
"ora": "3.4.0",
|
"ora": "3.4.0",
|
||||||
"pcre-to-regexp": "1.0.0",
|
"pcre-to-regexp": "1.0.0",
|
||||||
"pluralize": "7.0.0",
|
"pluralize": "7.0.0",
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import { Output } from '../../util/output';
|
|||||||
import * as ERRORS from '../../util/errors-ts';
|
import * as ERRORS from '../../util/errors-ts';
|
||||||
import assignAlias from '../../util/alias/assign-alias';
|
import assignAlias from '../../util/alias/assign-alias';
|
||||||
import Client from '../../util/client';
|
import Client from '../../util/client';
|
||||||
import formatNSTable from '../../util/format-ns-table';
|
|
||||||
import getDeploymentByIdOrHost from '../../util/deploy/get-deployment-by-id-or-host';
|
import getDeploymentByIdOrHost from '../../util/deploy/get-deployment-by-id-or-host';
|
||||||
import { getDeploymentForAlias } from '../../util/alias/get-deployment-by-alias';
|
import { getDeploymentForAlias } from '../../util/alias/get-deployment-by-alias';
|
||||||
import getScope from '../../util/get-scope';
|
import getScope from '../../util/get-scope';
|
||||||
@@ -226,29 +225,6 @@ function handleSetupDomainError<T>(
|
|||||||
output: Output,
|
output: Output,
|
||||||
error: SetupDomainError | T
|
error: SetupDomainError | T
|
||||||
): T | 1 {
|
): T | 1 {
|
||||||
if (
|
|
||||||
error instanceof ERRORS.DomainVerificationFailed ||
|
|
||||||
error instanceof ERRORS.DomainNsNotVerifiedForWildcard
|
|
||||||
) {
|
|
||||||
const { nsVerification, domain } = error.meta;
|
|
||||||
|
|
||||||
output.error(
|
|
||||||
`We could not alias since the domain ${domain} could not be verified due to the following reasons:\n`
|
|
||||||
);
|
|
||||||
output.print(
|
|
||||||
`Nameservers verification failed since we see a different set than the intended set:`
|
|
||||||
);
|
|
||||||
output.print(
|
|
||||||
`\n${formatNSTable(
|
|
||||||
nsVerification.intendedNameservers,
|
|
||||||
nsVerification.nameservers,
|
|
||||||
{ extraSpace: ' ' }
|
|
||||||
)}\n\n`
|
|
||||||
);
|
|
||||||
output.print(' Read more: https://err.sh/vercel/domain-verification\n');
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error instanceof ERRORS.DomainPermissionDenied) {
|
if (error instanceof ERRORS.DomainPermissionDenied) {
|
||||||
output.error(
|
output.error(
|
||||||
`You don't have permissions over domain ${chalk.underline(
|
`You don't have permissions over domain ${chalk.underline(
|
||||||
|
|||||||
411
packages/cli/src/commands/bisect/index.ts
Normal file
411
packages/cli/src/commands/bisect/index.ts
Normal file
@@ -0,0 +1,411 @@
|
|||||||
|
import open from 'open';
|
||||||
|
import boxen from 'boxen';
|
||||||
|
import execa from 'execa';
|
||||||
|
import plural from 'pluralize';
|
||||||
|
import inquirer from 'inquirer';
|
||||||
|
import { resolve } from 'path';
|
||||||
|
import chalk, { Chalk } from 'chalk';
|
||||||
|
import { URLSearchParams, parse } from 'url';
|
||||||
|
|
||||||
|
import sleep from '../../util/sleep';
|
||||||
|
import formatDate from '../../util/format-date';
|
||||||
|
import link from '../../util/output/link';
|
||||||
|
import logo from '../../util/output/logo';
|
||||||
|
import getArgs from '../../util/get-args';
|
||||||
|
import Client from '../../util/client';
|
||||||
|
import { getPkgName } from '../../util/pkg-name';
|
||||||
|
import { Output } from '../../util/output';
|
||||||
|
import { Deployment, PaginationOptions } from '../../types';
|
||||||
|
|
||||||
|
interface DeploymentV6
|
||||||
|
extends Pick<
|
||||||
|
Deployment,
|
||||||
|
'url' | 'target' | 'projectId' | 'ownerId' | 'meta' | 'inspectorUrl'
|
||||||
|
> {
|
||||||
|
createdAt: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Deployments {
|
||||||
|
deployments: DeploymentV6[];
|
||||||
|
pagination: PaginationOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pkgName = getPkgName();
|
||||||
|
|
||||||
|
const help = () => {
|
||||||
|
console.log(`
|
||||||
|
${chalk.bold(`${logo} ${pkgName} bisect`)} [options]
|
||||||
|
|
||||||
|
${chalk.dim('Options:')}
|
||||||
|
|
||||||
|
-h, --help Output usage information
|
||||||
|
-d, --debug Debug mode [off]
|
||||||
|
-b, --bad Known bad URL
|
||||||
|
-g, --good Known good URL
|
||||||
|
-o, --open Automatically open each URL in the browser
|
||||||
|
-p, --path Subpath of the deployment URL to test
|
||||||
|
-r, --run Test script to run for each deployment
|
||||||
|
|
||||||
|
${chalk.dim('Examples:')}
|
||||||
|
|
||||||
|
${chalk.gray('–')} Bisect the current project interactively
|
||||||
|
|
||||||
|
${chalk.cyan(`$ ${pkgName} bisect`)}
|
||||||
|
|
||||||
|
${chalk.gray('–')} Bisect with a known bad deployment
|
||||||
|
|
||||||
|
${chalk.cyan(`$ ${pkgName} bisect --bad example-310pce9i0.vercel.app`)}
|
||||||
|
|
||||||
|
${chalk.gray('–')} Bisect specifying a deployment that was working 3 days ago
|
||||||
|
|
||||||
|
${chalk.cyan(`$ ${pkgName} bisect --good 3d`)}
|
||||||
|
|
||||||
|
${chalk.gray('–')} Automated bisect with a run script
|
||||||
|
|
||||||
|
${chalk.cyan(`$ ${pkgName} bisect --run ./test.sh`)}
|
||||||
|
`);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default async function main(client: Client): Promise<number> {
|
||||||
|
const { output } = client;
|
||||||
|
|
||||||
|
const argv = getArgs(client.argv.slice(2), {
|
||||||
|
'--bad': String,
|
||||||
|
'-b': '--bad',
|
||||||
|
'--good': String,
|
||||||
|
'-g': '--good',
|
||||||
|
'--open': Boolean,
|
||||||
|
'-o': '--open',
|
||||||
|
'--path': String,
|
||||||
|
'-p': '--path',
|
||||||
|
'--run': String,
|
||||||
|
'-r': '--run',
|
||||||
|
});
|
||||||
|
|
||||||
|
if (argv['--help']) {
|
||||||
|
help();
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
let bad =
|
||||||
|
argv['--bad'] ||
|
||||||
|
(await prompt(output, `Specify a URL where the bug occurs:`));
|
||||||
|
let good =
|
||||||
|
argv['--good'] ||
|
||||||
|
(await prompt(output, `Specify a URL where the bug does not occur:`));
|
||||||
|
let subpath = argv['--path'] || '';
|
||||||
|
let run = argv['--run'] || '';
|
||||||
|
const openEnabled = argv['--open'] || false;
|
||||||
|
|
||||||
|
if (run) {
|
||||||
|
run = resolve(run);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bad.startsWith('https://')) {
|
||||||
|
bad = `https://${bad}`;
|
||||||
|
}
|
||||||
|
let parsed = parse(bad);
|
||||||
|
if (!parsed.hostname) {
|
||||||
|
output.error('Invalid input: no hostname provided');
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
bad = parsed.hostname;
|
||||||
|
if (typeof parsed.path === 'string' && parsed.path !== '/') {
|
||||||
|
if (subpath && subpath !== parsed.path) {
|
||||||
|
output.note(
|
||||||
|
`Ignoring subpath ${chalk.bold(
|
||||||
|
parsed.path
|
||||||
|
)} in favor of \`--path\` argument ${chalk.bold(subpath)}`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
subpath = parsed.path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const badDeploymentPromise = getDeployment(client, bad).catch(err => err);
|
||||||
|
|
||||||
|
if (!good.startsWith('https://')) {
|
||||||
|
good = `https://${good}`;
|
||||||
|
}
|
||||||
|
parsed = parse(good);
|
||||||
|
if (!parsed.hostname) {
|
||||||
|
output.error('Invalid input: no hostname provided');
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
good = parsed.hostname;
|
||||||
|
if (
|
||||||
|
typeof parsed.path === 'string' &&
|
||||||
|
parsed.path !== '/' &&
|
||||||
|
subpath &&
|
||||||
|
subpath !== parsed.path
|
||||||
|
) {
|
||||||
|
output.note(
|
||||||
|
`Ignoring subpath ${chalk.bold(
|
||||||
|
parsed.path
|
||||||
|
)} which does not match ${chalk.bold(subpath)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const goodDeploymentPromise = getDeployment(client, good).catch(err => err);
|
||||||
|
|
||||||
|
if (!subpath) {
|
||||||
|
subpath = await prompt(
|
||||||
|
output,
|
||||||
|
`Specify the URL subpath where the bug occurs:`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
output.spinner('Retrieving deployments…');
|
||||||
|
const [badDeployment, goodDeployment] = await Promise.all([
|
||||||
|
badDeploymentPromise,
|
||||||
|
goodDeploymentPromise,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (badDeployment) {
|
||||||
|
if (badDeployment instanceof Error) {
|
||||||
|
badDeployment.message += ` "${bad}"`;
|
||||||
|
output.prettyError(badDeployment);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
bad = badDeployment.url;
|
||||||
|
} else {
|
||||||
|
output.error(`Failed to retrieve ${chalk.bold('bad')} Deployment: ${bad}`);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { projectId } = badDeployment;
|
||||||
|
|
||||||
|
if (goodDeployment) {
|
||||||
|
if (goodDeployment instanceof Error) {
|
||||||
|
goodDeployment.message += ` "${good}"`;
|
||||||
|
output.prettyError(goodDeployment);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
good = goodDeployment.url;
|
||||||
|
} else {
|
||||||
|
output.error(
|
||||||
|
`Failed to retrieve ${chalk.bold('good')} Deployment: ${good}`
|
||||||
|
);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (projectId !== goodDeployment.projectId) {
|
||||||
|
output.error(`Good and Bad deployments must be from the same Project`);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (badDeployment.createdAt < goodDeployment.createdAt) {
|
||||||
|
output.error(`Good deployment must be older than the Bad deployment`);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (badDeployment.target !== goodDeployment.target) {
|
||||||
|
output.error(
|
||||||
|
`Bad deployment target "${badDeployment.target || 'preview'}" does not match good deployment target "${goodDeployment.target || 'preview'}"`
|
||||||
|
);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch all the project's "READY" deployments with the pagination API
|
||||||
|
let deployments: DeploymentV6[] = [];
|
||||||
|
let next: number | undefined = badDeployment.createdAt + 1;
|
||||||
|
do {
|
||||||
|
const query = new URLSearchParams();
|
||||||
|
query.set('projectId', projectId);
|
||||||
|
if (badDeployment.target) {
|
||||||
|
query.set('target', badDeployment.target);
|
||||||
|
}
|
||||||
|
query.set('limit', '100');
|
||||||
|
query.set('state', 'READY');
|
||||||
|
if (next) {
|
||||||
|
query.set('until', String(next));
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await client.fetch<Deployments>(`/v6/deployments?${query}`, {
|
||||||
|
accountId: badDeployment.ownerId,
|
||||||
|
});
|
||||||
|
|
||||||
|
next = res.pagination.next;
|
||||||
|
|
||||||
|
let newDeployments = res.deployments;
|
||||||
|
|
||||||
|
// If we have the "good" deployment in this chunk, then we're done
|
||||||
|
for (let i = 0; i < newDeployments.length; i++) {
|
||||||
|
if (newDeployments[i].url === good) {
|
||||||
|
newDeployments = newDeployments.slice(0, i + 1);
|
||||||
|
next = undefined;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deployments = deployments.concat(newDeployments);
|
||||||
|
|
||||||
|
if (next) {
|
||||||
|
// Small sleep to avoid rate limiting
|
||||||
|
await sleep(100);
|
||||||
|
}
|
||||||
|
} while (next);
|
||||||
|
|
||||||
|
if (!deployments.length) {
|
||||||
|
output.error(
|
||||||
|
'Cannot bisect because this project does not have any deployments'
|
||||||
|
);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The first deployment is the one that was marked
|
||||||
|
// as "bad", so that one does not need to be tested
|
||||||
|
let lastBad = deployments.shift()!;
|
||||||
|
|
||||||
|
while (deployments.length > 0) {
|
||||||
|
// Add a blank space before the next step
|
||||||
|
output.print('\n');
|
||||||
|
const middleIndex = Math.floor(deployments.length / 2);
|
||||||
|
const deployment = deployments[middleIndex];
|
||||||
|
const rem = plural('deployment', deployments.length, true);
|
||||||
|
const steps = Math.floor(Math.log2(deployments.length));
|
||||||
|
const pSteps = plural('step', steps, true);
|
||||||
|
output.log(
|
||||||
|
chalk.magenta(
|
||||||
|
`${chalk.bold(
|
||||||
|
'Bisecting:'
|
||||||
|
)} ${rem} left to test after this (roughly ${pSteps})`
|
||||||
|
),
|
||||||
|
chalk.magenta
|
||||||
|
);
|
||||||
|
const testUrl = `https://${deployment.url}${subpath}`;
|
||||||
|
output.log(`${chalk.bold('Deployment URL:')} ${link(testUrl)}`);
|
||||||
|
|
||||||
|
output.log(`${chalk.bold('Date:')} ${formatDate(deployment.createdAt)}`);
|
||||||
|
|
||||||
|
const commit = getCommit(deployment);
|
||||||
|
if (commit) {
|
||||||
|
const shortSha = commit.sha.substring(0, 7);
|
||||||
|
const firstLine = commit.message.split('\n')[0];
|
||||||
|
output.log(`${chalk.bold('Commit:')} [${shortSha}] ${firstLine}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
let action: string;
|
||||||
|
if (run) {
|
||||||
|
const proc = await execa(run, [testUrl], {
|
||||||
|
stdio: 'inherit',
|
||||||
|
reject: false,
|
||||||
|
env: {
|
||||||
|
...process.env,
|
||||||
|
HOST: deployment.url,
|
||||||
|
URL: testUrl,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (proc instanceof Error && typeof proc.exitCode !== 'number') {
|
||||||
|
// Script does not exist or is not executable, so exit
|
||||||
|
output.prettyError(proc);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
const { exitCode } = proc;
|
||||||
|
let color: Chalk;
|
||||||
|
if (exitCode === 0) {
|
||||||
|
color = chalk.green;
|
||||||
|
action = 'good';
|
||||||
|
} else if (exitCode === 125) {
|
||||||
|
action = 'skip';
|
||||||
|
color = chalk.grey;
|
||||||
|
} else {
|
||||||
|
action = 'bad';
|
||||||
|
color = chalk.red;
|
||||||
|
}
|
||||||
|
output.log(
|
||||||
|
`Run script returned exit code ${chalk.bold(String(exitCode))}: ${color(
|
||||||
|
action
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
if (openEnabled) {
|
||||||
|
await open(testUrl);
|
||||||
|
}
|
||||||
|
const answer = await inquirer.prompt({
|
||||||
|
type: 'expand',
|
||||||
|
name: 'action',
|
||||||
|
message: 'Select an action:',
|
||||||
|
choices: [
|
||||||
|
{ key: 'g', name: 'Good', value: 'good' },
|
||||||
|
{ key: 'b', name: 'Bad', value: 'bad' },
|
||||||
|
{ key: 's', name: 'Skip', value: 'skip' },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
action = answer.action;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action === 'good') {
|
||||||
|
deployments = deployments.slice(0, middleIndex);
|
||||||
|
} else if (action === 'bad') {
|
||||||
|
lastBad = deployment;
|
||||||
|
deployments = deployments.slice(middleIndex + 1);
|
||||||
|
} else if (action === 'skip') {
|
||||||
|
deployments.splice(middleIndex, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output.print('\n');
|
||||||
|
|
||||||
|
let result = [
|
||||||
|
chalk.bold(
|
||||||
|
`The first bad deployment is: ${link(`https://${lastBad.url}`)}`
|
||||||
|
),
|
||||||
|
'',
|
||||||
|
` ${chalk.bold('Date:')} ${formatDate(lastBad.createdAt)}`,
|
||||||
|
];
|
||||||
|
|
||||||
|
const commit = getCommit(lastBad);
|
||||||
|
if (commit) {
|
||||||
|
const shortSha = commit.sha.substring(0, 7);
|
||||||
|
const firstLine = commit.message.split('\n')[0];
|
||||||
|
result.push(` ${chalk.bold('Commit:')} [${shortSha}] ${firstLine}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push(`${chalk.bold('Inspect:')} ${link(lastBad.inspectorUrl)}`);
|
||||||
|
|
||||||
|
output.print(boxen(result.join('\n'), { padding: 1 }));
|
||||||
|
output.print('\n');
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDeployment(
|
||||||
|
client: Client,
|
||||||
|
hostname: string
|
||||||
|
): Promise<DeploymentV6> {
|
||||||
|
const query = new URLSearchParams();
|
||||||
|
query.set('url', hostname);
|
||||||
|
query.set('resolve', '1');
|
||||||
|
query.set('noState', '1');
|
||||||
|
return client.fetch<DeploymentV6>(`/v10/deployments/get?${query}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCommit(deployment: DeploymentV6) {
|
||||||
|
const sha =
|
||||||
|
deployment.meta?.githubCommitSha ||
|
||||||
|
deployment.meta?.gitlabCommitSha ||
|
||||||
|
deployment.meta?.bitbucketCommitSha;
|
||||||
|
if (!sha) return null;
|
||||||
|
const message =
|
||||||
|
deployment.meta?.githubCommitMessage ||
|
||||||
|
deployment.meta?.gitlabCommitMessage ||
|
||||||
|
deployment.meta?.bitbucketCommitMessage;
|
||||||
|
return { sha, message };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function prompt(output: Output, message: string): Promise<string> {
|
||||||
|
// eslint-disable-next-line no-constant-condition
|
||||||
|
while (true) {
|
||||||
|
const { val } = await inquirer.prompt({
|
||||||
|
type: 'input',
|
||||||
|
name: 'val',
|
||||||
|
message,
|
||||||
|
});
|
||||||
|
if (val) {
|
||||||
|
return val;
|
||||||
|
} else {
|
||||||
|
output.error('A value must be specified');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,6 +28,7 @@ export const help = () => `
|
|||||||
${chalk.dim('Advanced')}
|
${chalk.dim('Advanced')}
|
||||||
|
|
||||||
rm | remove [id] Removes a deployment
|
rm | remove [id] Removes a deployment
|
||||||
|
bisect Use binary search to find the deployment that introduced a bug
|
||||||
domains [name] Manages your domain names
|
domains [name] Manages your domain names
|
||||||
projects Manages your Projects
|
projects Manages your Projects
|
||||||
dns [name] Manages your DNS records
|
dns [name] Manages your DNS records
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ export default new Map([
|
|||||||
['alias', 'alias'],
|
['alias', 'alias'],
|
||||||
['aliases', 'alias'],
|
['aliases', 'alias'],
|
||||||
['billing', 'billing'],
|
['billing', 'billing'],
|
||||||
|
['bisect', 'bisect'],
|
||||||
['build', 'build'],
|
['build', 'build'],
|
||||||
['cc', 'billing'],
|
['cc', 'billing'],
|
||||||
['cert', 'certs'],
|
['cert', 'certs'],
|
||||||
|
|||||||
@@ -627,6 +627,9 @@ const main = async () => {
|
|||||||
case 'billing':
|
case 'billing':
|
||||||
func = await import('./commands/billing');
|
func = await import('./commands/billing');
|
||||||
break;
|
break;
|
||||||
|
case 'bisect':
|
||||||
|
func = await import('./commands/bisect');
|
||||||
|
break;
|
||||||
case 'build':
|
case 'build':
|
||||||
func = await import('./commands/build');
|
func = await import('./commands/build');
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -85,10 +85,6 @@ export type Domain = {
|
|||||||
transferredAt?: number | null;
|
transferredAt?: number | null;
|
||||||
orderedAt?: number;
|
orderedAt?: number;
|
||||||
serviceType: 'zeit.world' | 'external' | 'na';
|
serviceType: 'zeit.world' | 'external' | 'na';
|
||||||
verified: boolean;
|
|
||||||
nsVerifiedAt: number | null;
|
|
||||||
txtVerifiedAt: number | null;
|
|
||||||
verificationRecord: string;
|
|
||||||
nameservers: string[];
|
nameservers: string[];
|
||||||
intendedNameservers: string[];
|
intendedNameservers: string[];
|
||||||
creator: {
|
creator: {
|
||||||
@@ -133,6 +129,13 @@ export type Deployment = {
|
|||||||
created: number;
|
created: number;
|
||||||
createdAt: number;
|
createdAt: number;
|
||||||
creator: { uid: string; username: string };
|
creator: { uid: string; username: string };
|
||||||
|
target: string | null;
|
||||||
|
ownerId: string;
|
||||||
|
projectId: string;
|
||||||
|
inspectorUrl: string;
|
||||||
|
meta: {
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Alias = {
|
export type Alias = {
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import {
|
|||||||
BuildResultV3,
|
BuildResultV3,
|
||||||
BuilderOutputs,
|
BuilderOutputs,
|
||||||
EnvConfigs,
|
EnvConfigs,
|
||||||
|
BuiltLambda,
|
||||||
} from './types';
|
} from './types';
|
||||||
import { normalizeRoutes } from '@vercel/routing-utils';
|
import { normalizeRoutes } from '@vercel/routing-utils';
|
||||||
import getUpdateCommand from '../get-update-command';
|
import getUpdateCommand from '../get-update-command';
|
||||||
@@ -288,7 +289,7 @@ export async function executeBuild(
|
|||||||
// subclass type instances.
|
// subclass type instances.
|
||||||
for (const name of Object.keys(output)) {
|
for (const name of Object.keys(output)) {
|
||||||
const obj = output[name] as File;
|
const obj = output[name] as File;
|
||||||
let lambda: Lambda;
|
let lambda: BuiltLambda;
|
||||||
let fileRef: FileFsRef;
|
let fileRef: FileFsRef;
|
||||||
let fileBlob: FileBlob;
|
let fileBlob: FileBlob;
|
||||||
switch (obj.type) {
|
switch (obj.type) {
|
||||||
@@ -302,7 +303,7 @@ export async function executeBuild(
|
|||||||
output[name] = fileBlob;
|
output[name] = fileBlob;
|
||||||
break;
|
break;
|
||||||
case 'Lambda':
|
case 'Lambda':
|
||||||
lambda = Object.assign(Object.create(Lambda.prototype), obj) as Lambda;
|
lambda = Object.assign(Object.create(Lambda.prototype), obj);
|
||||||
// Convert the JSON-ified Buffer object back into an actual Buffer
|
// Convert the JSON-ified Buffer object back into an actual Buffer
|
||||||
lambda.zipBuffer = Buffer.from((obj as any).zipBuffer.data);
|
lambda.zipBuffer = Buffer.from((obj as any).zipBuffer.data);
|
||||||
output[name] = lambda;
|
output[name] = lambda;
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ export interface BuilderInputs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface BuiltLambda extends Lambda {
|
export interface BuiltLambda extends Lambda {
|
||||||
|
zipBuffer: Buffer;
|
||||||
fn?: FunLambda;
|
fn?: FunLambda;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export async function getDomain(
|
|||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
const { domain } = await client.fetch<Response>(
|
const { domain } = await client.fetch<Response>(
|
||||||
`/v4/domains/${domainName}`
|
`/v5/domains/${domainName}`
|
||||||
);
|
);
|
||||||
|
|
||||||
return domain;
|
return domain;
|
||||||
|
|||||||
@@ -7,9 +7,7 @@ import addDomain from './add-domain';
|
|||||||
import Client from '../client';
|
import Client from '../client';
|
||||||
import maybeGetDomainByName from './maybe-get-domain-by-name';
|
import maybeGetDomainByName from './maybe-get-domain-by-name';
|
||||||
import purchaseDomainIfAvailable from './purchase-domain-if-available';
|
import purchaseDomainIfAvailable from './purchase-domain-if-available';
|
||||||
import verifyDomain from './verify-domain';
|
|
||||||
import extractDomain from '../alias/extract-domain';
|
import extractDomain from '../alias/extract-domain';
|
||||||
import isWildcardAlias from '../alias/is-wildcard-alias';
|
|
||||||
|
|
||||||
export default async function setupDomain(
|
export default async function setupDomain(
|
||||||
output: Output,
|
output: Output,
|
||||||
@@ -27,36 +25,6 @@ export default async function setupDomain(
|
|||||||
if (info) {
|
if (info) {
|
||||||
const { name: domain } = info;
|
const { name: domain } = info;
|
||||||
output.debug(`Domain ${domain} found for the given context`);
|
output.debug(`Domain ${domain} found for the given context`);
|
||||||
if (!info.verified || (!info.nsVerifiedAt && isWildcardAlias(alias))) {
|
|
||||||
output.debug(
|
|
||||||
`Domain ${domain} is not verified, trying to perform a verification`
|
|
||||||
);
|
|
||||||
const verificationResult = await verifyDomain(
|
|
||||||
client,
|
|
||||||
domain,
|
|
||||||
contextName
|
|
||||||
);
|
|
||||||
if (verificationResult instanceof ERRORS.DomainVerificationFailed) {
|
|
||||||
output.debug(`Domain ${domain} verification failed`);
|
|
||||||
return verificationResult;
|
|
||||||
}
|
|
||||||
if (!verificationResult.nsVerifiedAt && isWildcardAlias(alias)) {
|
|
||||||
return new ERRORS.DomainNsNotVerifiedForWildcard({
|
|
||||||
domain,
|
|
||||||
nsVerification: {
|
|
||||||
intendedNameservers: verificationResult.intendedNameservers,
|
|
||||||
nameservers: verificationResult.nameservers,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
output.debug(`Domain ${domain} successfuly verified`);
|
|
||||||
return maybeGetDomainByName(client, contextName, domain) as Promise<
|
|
||||||
Domain
|
|
||||||
>;
|
|
||||||
}
|
|
||||||
|
|
||||||
output.debug(`Domain ${domain} is already verified`);
|
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,21 +60,6 @@ export default async function setupDomain(
|
|||||||
return addResult;
|
return addResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!addResult.verified) {
|
|
||||||
const verificationResult = await verifyDomain(
|
|
||||||
client,
|
|
||||||
domain,
|
|
||||||
contextName
|
|
||||||
);
|
|
||||||
if (verificationResult instanceof ERRORS.DomainVerificationFailed) {
|
|
||||||
output.debug(`Domain ${domain} was added but it couldn't be verified`);
|
|
||||||
return verificationResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
output.debug(`Domain ${domain} successfuly added and manually verified`);
|
|
||||||
return verificationResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
output.debug(
|
output.debug(
|
||||||
`Domain ${domain} successfuly added and automatically verified`
|
`Domain ${domain} successfuly added and automatically verified`
|
||||||
);
|
);
|
||||||
@@ -120,23 +73,6 @@ export default async function setupDomain(
|
|||||||
aliasDomain
|
aliasDomain
|
||||||
)) as Domain;
|
)) as Domain;
|
||||||
const { name: domain } = purchasedDomain;
|
const { name: domain } = purchasedDomain;
|
||||||
if (!purchasedDomain.verified) {
|
|
||||||
const verificationResult = await verifyDomain(client, domain, contextName);
|
|
||||||
if (verificationResult instanceof ERRORS.DomainVerificationFailed) {
|
|
||||||
output.debug(
|
|
||||||
`Domain ${domain} was purchased but verification is still pending`
|
|
||||||
);
|
|
||||||
return new ERRORS.DomainVerificationFailed({
|
|
||||||
domain: verificationResult.meta.domain,
|
|
||||||
nsVerification: verificationResult.meta.nsVerification,
|
|
||||||
txtVerification: verificationResult.meta.txtVerification,
|
|
||||||
purchased: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
output.debug(`Domain ${domain} was purchased and it was manually verified`);
|
|
||||||
return maybeGetDomainByName(client, contextName, domain) as Promise<Domain>;
|
|
||||||
}
|
|
||||||
|
|
||||||
output.debug(
|
output.debug(
|
||||||
`Domain ${domain} was purchased and it is automatically verified`
|
`Domain ${domain} was purchased and it is automatically verified`
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
import chalk from 'chalk';
|
|
||||||
import retry from 'async-retry';
|
|
||||||
import { Domain } from '../../types';
|
|
||||||
import * as ERRORS from '../errors-ts';
|
|
||||||
import Client from '../client';
|
|
||||||
|
|
||||||
export default async function verifyDomain(
|
|
||||||
client: Client,
|
|
||||||
domainName: string,
|
|
||||||
contextName: string
|
|
||||||
) {
|
|
||||||
client.output.spinner(
|
|
||||||
`Verifying domain ${domainName} under ${chalk.bold(contextName)}`
|
|
||||||
);
|
|
||||||
try {
|
|
||||||
const { domain } = await performVerifyDomain(client, domainName);
|
|
||||||
return domain;
|
|
||||||
} catch (error) {
|
|
||||||
if (error.code === 'verification_failed') {
|
|
||||||
return new ERRORS.DomainVerificationFailed({
|
|
||||||
purchased: false,
|
|
||||||
domain: error.name as string,
|
|
||||||
nsVerification: error.nsVerification as ERRORS.NSVerificationError,
|
|
||||||
txtVerification: error.txtVerification as ERRORS.TXTVerificationError,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Response = {
|
|
||||||
domain: Domain;
|
|
||||||
};
|
|
||||||
|
|
||||||
async function performVerifyDomain(client: Client, domain: string) {
|
|
||||||
return retry(
|
|
||||||
async () =>
|
|
||||||
client.fetch<Response>(
|
|
||||||
`/v4/domains/${encodeURIComponent(domain)}/verify`,
|
|
||||||
{
|
|
||||||
body: {},
|
|
||||||
method: 'POST',
|
|
||||||
}
|
|
||||||
),
|
|
||||||
{ retries: 5, maxTimeout: 8000 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -260,31 +260,6 @@ export type TXTVerificationError = {
|
|||||||
values: string[];
|
values: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* This error is returned when the domain is not verified by nameservers for wildcard alias.
|
|
||||||
*/
|
|
||||||
export class DomainNsNotVerifiedForWildcard extends NowError<
|
|
||||||
'DOMAIN_NS_NOT_VERIFIED_FOR_WILDCARD',
|
|
||||||
{
|
|
||||||
domain: string;
|
|
||||||
nsVerification: NSVerificationError;
|
|
||||||
}
|
|
||||||
> {
|
|
||||||
constructor({
|
|
||||||
domain,
|
|
||||||
nsVerification,
|
|
||||||
}: {
|
|
||||||
domain: string;
|
|
||||||
nsVerification: NSVerificationError;
|
|
||||||
}) {
|
|
||||||
super({
|
|
||||||
code: 'DOMAIN_NS_NOT_VERIFIED_FOR_WILDCARD',
|
|
||||||
meta: { domain, nsVerification },
|
|
||||||
message: `The domain ${domain} is not verified by nameservers for wildcard alias.`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used when a domain is validated because we tried to add it to an account
|
* Used when a domain is validated because we tried to add it to an account
|
||||||
* via API or for any other reason.
|
* via API or for any other reason.
|
||||||
|
|||||||
5
packages/cli/test/integration.js
vendored
5
packages/cli/test/integration.js
vendored
@@ -7,9 +7,10 @@ import { Readable } from 'stream';
|
|||||||
import { homedir } from 'os';
|
import { homedir } from 'os';
|
||||||
import _execa from 'execa';
|
import _execa from 'execa';
|
||||||
import XDGAppPaths from 'xdg-app-paths';
|
import XDGAppPaths from 'xdg-app-paths';
|
||||||
import fetch from 'node-fetch';
|
import nodeFetch from 'node-fetch';
|
||||||
import tmp from 'tmp-promise';
|
import tmp from 'tmp-promise';
|
||||||
import retry from 'async-retry';
|
import retry from 'async-retry';
|
||||||
|
import createFetchRetry from '@vercel/fetch-retry';
|
||||||
import fs, {
|
import fs, {
|
||||||
writeFile,
|
writeFile,
|
||||||
readFile,
|
readFile,
|
||||||
@@ -24,6 +25,8 @@ import pkg from '../package';
|
|||||||
import prepareFixtures from './helpers/prepare';
|
import prepareFixtures from './helpers/prepare';
|
||||||
import { fetchTokenWithRetry } from '../../../test/lib/deployment/now-deploy';
|
import { fetchTokenWithRetry } from '../../../test/lib/deployment/now-deploy';
|
||||||
|
|
||||||
|
const fetch = createFetchRetry(nodeFetch);
|
||||||
|
|
||||||
// log command when running `execa`
|
// log command when running `execa`
|
||||||
function execa(file, args, options) {
|
function execa(file, args, options) {
|
||||||
console.log(`$ vercel ${args.join(' ')}`);
|
console.log(`$ vercel ${args.join(' ')}`);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vercel/client",
|
"name": "@vercel/client",
|
||||||
"version": "10.2.3-canary.51",
|
"version": "10.2.3-canary.53",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"typings": "dist/index.d.ts",
|
"typings": "dist/index.d.ts",
|
||||||
"homepage": "https://vercel.com",
|
"homepage": "https://vercel.com",
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vercel/build-utils": "2.13.1-canary.0",
|
"@vercel/build-utils": "2.13.1-canary.2",
|
||||||
"@zeit/fetch": "5.2.0",
|
"@zeit/fetch": "5.2.0",
|
||||||
"async-retry": "1.2.3",
|
"async-retry": "1.2.3",
|
||||||
"async-sema": "3.0.0",
|
"async-sema": "3.0.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vercel/frameworks",
|
"name": "@vercel/frameworks",
|
||||||
"version": "0.5.1-canary.20",
|
"version": "0.5.1-canary.21",
|
||||||
"main": "./dist/frameworks.js",
|
"main": "./dist/frameworks.js",
|
||||||
"types": "./dist/frameworks.d.ts",
|
"types": "./dist/frameworks.d.ts",
|
||||||
"files": [
|
"files": [
|
||||||
|
|||||||
@@ -1941,7 +1941,7 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
devCommand: {
|
devCommand: {
|
||||||
placeholder: 'vite',
|
placeholder: 'vite',
|
||||||
value: 'vite',
|
value: 'vite --port $PORT',
|
||||||
},
|
},
|
||||||
outputDirectory: {
|
outputDirectory: {
|
||||||
value: 'dist',
|
value: 'dist',
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vercel/go",
|
"name": "@vercel/go",
|
||||||
"version": "1.2.4-canary.5",
|
"version": "1.2.4-canary.7",
|
||||||
"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",
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
"@types/fs-extra": "^5.0.5",
|
"@types/fs-extra": "^5.0.5",
|
||||||
"@types/node-fetch": "^2.3.0",
|
"@types/node-fetch": "^2.3.0",
|
||||||
"@types/tar": "^4.0.0",
|
"@types/tar": "^4.0.0",
|
||||||
"@vercel/build-utils": "2.13.1-canary.0",
|
"@vercel/build-utils": "2.13.1-canary.2",
|
||||||
"@vercel/ncc": "0.24.0",
|
"@vercel/ncc": "0.24.0",
|
||||||
"async-retry": "1.3.1",
|
"async-retry": "1.3.1",
|
||||||
"execa": "^1.0.0",
|
"execa": "^1.0.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "vercel-plugin-middleware",
|
"name": "vercel-plugin-middleware",
|
||||||
"version": "0.0.0-canary.25",
|
"version": "0.0.0-canary.27",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "./dist/index",
|
"main": "./dist/index",
|
||||||
"homepage": "",
|
"homepage": "",
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
"@types/node-fetch": "^2",
|
"@types/node-fetch": "^2",
|
||||||
"@types/ua-parser-js": "0.7.36",
|
"@types/ua-parser-js": "0.7.36",
|
||||||
"@types/uuid": "8.3.1",
|
"@types/uuid": "8.3.1",
|
||||||
"@vercel/build-utils": "2.13.1-canary.0",
|
"@vercel/build-utils": "2.13.1-canary.2",
|
||||||
"@vercel/ncc": "0.24.0",
|
"@vercel/ncc": "0.24.0",
|
||||||
"cookie": "0.4.1",
|
"cookie": "0.4.1",
|
||||||
"formdata-node": "4.3.1",
|
"formdata-node": "4.3.1",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vercel/node",
|
"name": "@vercel/node",
|
||||||
"version": "1.12.2-canary.8",
|
"version": "1.12.2-canary.10",
|
||||||
"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",
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
"@types/cookie": "0.3.3",
|
"@types/cookie": "0.3.3",
|
||||||
"@types/etag": "1.8.0",
|
"@types/etag": "1.8.0",
|
||||||
"@types/test-listen": "1.1.0",
|
"@types/test-listen": "1.1.0",
|
||||||
"@vercel/build-utils": "2.13.1-canary.0",
|
"@vercel/build-utils": "2.13.1-canary.2",
|
||||||
"@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",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"private": false,
|
"private": false,
|
||||||
"name": "vercel-plugin-go",
|
"name": "vercel-plugin-go",
|
||||||
"version": "1.0.0-canary.36",
|
"version": "1.0.0-canary.38",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"files": [
|
"files": [
|
||||||
@@ -17,8 +17,8 @@
|
|||||||
"prepublishOnly": "tsc"
|
"prepublishOnly": "tsc"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vercel/build-utils": "2.13.1-canary.0",
|
"@vercel/build-utils": "2.13.1-canary.2",
|
||||||
"@vercel/go": "1.2.4-canary.5"
|
"@vercel/go": "1.2.4-canary.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "vercel-plugin-node",
|
"name": "vercel-plugin-node",
|
||||||
"version": "1.12.2-canary.40",
|
"version": "1.12.2-canary.42",
|
||||||
"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",
|
||||||
@@ -34,7 +34,7 @@
|
|||||||
"@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",
|
"@types/yazl": "2.4.2",
|
||||||
"@vercel/build-utils": "2.13.1-canary.0",
|
"@vercel/build-utils": "2.13.1-canary.2",
|
||||||
"@vercel/fun": "1.0.3",
|
"@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",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"private": false,
|
"private": false,
|
||||||
"name": "vercel-plugin-python",
|
"name": "vercel-plugin-python",
|
||||||
"version": "1.0.0-canary.37",
|
"version": "1.0.0-canary.39",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"files": [
|
"files": [
|
||||||
@@ -17,8 +17,8 @@
|
|||||||
"prepublishOnly": "tsc"
|
"prepublishOnly": "tsc"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vercel/build-utils": "2.13.1-canary.0",
|
"@vercel/build-utils": "2.13.1-canary.2",
|
||||||
"@vercel/python": "2.1.2-canary.3"
|
"@vercel/python": "2.1.2-canary.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"private": false,
|
"private": false,
|
||||||
"name": "vercel-plugin-ruby",
|
"name": "vercel-plugin-ruby",
|
||||||
"version": "1.0.0-canary.36",
|
"version": "1.0.0-canary.38",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"files": [
|
"files": [
|
||||||
@@ -17,8 +17,8 @@
|
|||||||
"prepublishOnly": "tsc"
|
"prepublishOnly": "tsc"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vercel/build-utils": "2.13.1-canary.0",
|
"@vercel/build-utils": "2.13.1-canary.2",
|
||||||
"@vercel/ruby": "1.2.10-canary.1"
|
"@vercel/ruby": "1.2.10-canary.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vercel/python",
|
"name": "@vercel/python",
|
||||||
"version": "2.1.2-canary.3",
|
"version": "2.1.2-canary.5",
|
||||||
"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",
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/execa": "^0.9.0",
|
"@types/execa": "^0.9.0",
|
||||||
"@vercel/build-utils": "2.13.1-canary.0",
|
"@vercel/build-utils": "2.13.1-canary.2",
|
||||||
"@vercel/ncc": "0.24.0",
|
"@vercel/ncc": "0.24.0",
|
||||||
"execa": "^1.0.0",
|
"execa": "^1.0.0",
|
||||||
"typescript": "4.3.4"
|
"typescript": "4.3.4"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@vercel/ruby",
|
"name": "@vercel/ruby",
|
||||||
"author": "Nathan Cahill <nathan@nathancahill.com>",
|
"author": "Nathan Cahill <nathan@nathancahill.com>",
|
||||||
"version": "1.2.10-canary.1",
|
"version": "1.2.10-canary.3",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "./dist/index",
|
"main": "./dist/index",
|
||||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/ruby",
|
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/ruby",
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/fs-extra": "8.0.0",
|
"@types/fs-extra": "8.0.0",
|
||||||
"@types/semver": "6.0.0",
|
"@types/semver": "6.0.0",
|
||||||
"@vercel/build-utils": "2.13.1-canary.0",
|
"@vercel/build-utils": "2.13.1-canary.2",
|
||||||
"@vercel/ncc": "0.24.0",
|
"@vercel/ncc": "0.24.0",
|
||||||
"execa": "2.0.4",
|
"execa": "2.0.4",
|
||||||
"fs-extra": "^7.0.1",
|
"fs-extra": "^7.0.1",
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"$schema": "https://openapi.vercel.sh/vercel.json",
|
||||||
"rewrites": [
|
"rewrites": [
|
||||||
{
|
{
|
||||||
"source": "/",
|
"source": "/",
|
||||||
|
|||||||
16
yarn.lock
16
yarn.lock
@@ -2613,6 +2613,14 @@
|
|||||||
"@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/fetch-retry@5.0.3":
|
||||||
|
version "5.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vercel/fetch-retry/-/fetch-retry-5.0.3.tgz#cce5d23f6e64f6f525c24e2ac7c78f65d6c5b1f4"
|
||||||
|
integrity sha512-DIIoBY92r+sQ6iHSf5WjKiYvkdsDIMPWKYATlE0KcUAj2RV6SZK9UWpUzBRKsofXqedOqpVjrI0IE6AWL7JRtg==
|
||||||
|
dependencies:
|
||||||
|
async-retry "^1.3.1"
|
||||||
|
debug "^3.1.0"
|
||||||
|
|
||||||
"@vercel/fun@1.0.3":
|
"@vercel/fun@1.0.3":
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/@vercel/fun/-/fun-1.0.3.tgz#5e5c4921a3a3a35ee129ce063e6b3f5c4b1eae48"
|
resolved "https://registry.yarnpkg.com/@vercel/fun/-/fun-1.0.3.tgz#5e5c4921a3a3a35ee129ce063e6b3f5c4b1eae48"
|
||||||
@@ -9346,10 +9354,10 @@ onetime@^5.1.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
mimic-fn "^2.1.0"
|
mimic-fn "^2.1.0"
|
||||||
|
|
||||||
open@8.2.0:
|
open@8.4.0:
|
||||||
version "8.2.0"
|
version "8.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/open/-/open-8.2.0.tgz#d6a4788b00009a9d60df471ecb89842a15fdcfc1"
|
resolved "https://registry.yarnpkg.com/open/-/open-8.4.0.tgz#345321ae18f8138f82565a910fdc6b39e8c244f8"
|
||||||
integrity sha512-O8uInONB4asyY3qUcEytpgwxQG3O0fJ/hlssoUHsBboOIRVZzT6Wq+Rwj5nffbeUhOdMjpXeISpDDzHCMRDuOQ==
|
integrity sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==
|
||||||
dependencies:
|
dependencies:
|
||||||
define-lazy-prop "^2.0.0"
|
define-lazy-prop "^2.0.0"
|
||||||
is-docker "^2.1.1"
|
is-docker "^2.1.1"
|
||||||
|
|||||||
Reference in New Issue
Block a user