Compare commits

...

79 Commits

Author SHA1 Message Date
Nathan Rajlich
64ee4905cd Publish
- @now/node-server@0.5.0-canary.1
 - @now/node@0.5.0-canary.1
2019-02-20 13:53:37 -08:00
Nathan Rajlich
e50dd7e50a Bump now/node and now/node-server to v0.5.0 canary 2019-02-20 13:53:00 -08:00
Nathan Rajlich
6101ba9d95 [now-node-server] Update @now/node-bridge to v1.0.0-canary.2 (#231)
Update `@now/node-server` to use the latest `@now/node-bridge`, which
removes the hard-coded port 3000 that was previously in-place,
which was problematic for `now dev`. Now the `http.Server`
instance listens on an ephemeral port which is detected by the
Now node runtime.
2019-02-20 13:47:17 -08:00
Nathan Rajlich
8dc0c92c58 [now-node] Migrate to TypeScript and use @now/node-bridge v1 (#212)
This moves `@now/node` to being implemented in TypeScript.

It also updates it to use the latest `@now/node-bridge`, which
removes the hard-coded port 3000 that was previously in-place,
which was problematic for `now dev`. Now the `http.Server`
instance listens on an ephemeral port which is detected by the
Now node runtime.
2019-02-20 12:33:21 -08:00
Nathan Rajlich
44c9f3765a Publish
- @now/node-bridge@1.0.0-canary.2
 - @now/node-server@0.4.27-canary.7
 - @now/node@0.4.29-canary.7
2019-02-19 20:49:58 -08:00
Nathan Rajlich
92c05ca338 Pin @now/node-bridge to v0.1.11-canary.0
To address: https://github.com/zeit/now-builders/pull/224#issuecomment-465410905
2019-02-19 20:26:38 -08:00
Nathan Rajlich
069b557906 Publish
- @now/node-bridge@1.0.0-canary.1
2019-02-19 17:26:04 -08:00
Nathan Rajlich
692a0df909 Publish
- @now/node-bridge@0.1.11-canary.1
 - @now/node-server@0.4.27-canary.6
 - @now/node@0.4.29-canary.6
2019-02-19 16:33:18 -08:00
Nathan Rajlich
aeafeb5441 [now-node-bridge] Refactor API to be http.Server focused (#224)
* [now-node-bridge] Refactor API to be `http.Server` focused

This commit refactors the `@now/node-bridge` helper module to work with
`http.Server` instances directly, instead of expecting the `port` to be
set.

The `Bridge` instance calls the `listen()` function on the server instance
which binds to an ephemeral port. This is especially important for
`now dev`, where using a hard-coded port will cause port conflicts for
multiple lambdas using the same builder.

Also converts to TypeScript and adds some basic unit tests.

Example usage:

```js
const server = new Server(() => {});
const bridge = new Bridge(server);
bridge.listen();

const info = await bridge.listening;
assert.equal(info.address, '127.0.0.1');
assert.equal(typeof info.port, 'number');
```

* Update `yarn.lock`

* Add `pretest` script

* Enable TypeScript `strict` mode

* Throw if a string is returned from `server.address()`

Defensive programming ftw

Co-Authored-By: TooTallNate <n@n8.io>

* Prettier

* Prettier

* Fixes

* Attempt to fix CI

* Add `files` array to package.json

* Check for the `Action` property to avoid type casting

Co-Authored-By: TooTallNate <n@n8.io>

* Split the normalizing functions into separate ones

Also adds additional unit tests.

* export the `NowProxyRequest` and `NowProxyResponse`

* Remove last `as` casting

* Move some up

* Debug CircleCI tests :/

* Fix "Invoke" check

* Attempt to fix CircleCI again

* Fix bad

* Convert tests to use `jest`
2019-02-19 16:17:58 -08:00
Nathan Rajlich
a09d5fb355 Add publish.sh script for CircleCI (#228)
* Add `publish.sh` script for CircleCI

Centralize the publishing logic for CircleCI to use.

There was a bug in the previous "Potentially publish stable release"
branch, such that it would be executed for non-tagged commits. The new
publish script checks if there are indeed any tags for the commit, and
bails if there are none.

As an additional bonus, there's now only one publish step in the
CircleCI config, because the publish script determines stable vs. canary
based on the tag name.

* Remove `echo`
2019-02-19 14:45:04 -08:00
Nathan Rajlich
d8017aa9aa Publish
- @now/python@0.0.41-canary.2
2019-02-19 12:14:49 -08:00
Honza Javorek
702f56b9b5 [now-python] Add missing base64 import and fix coding style (#226) 2019-02-19 12:11:12 -08:00
Nathan Rajlich
183b117152 Publish
- @now/python@0.0.41-canary.1
2019-02-19 11:29:26 -08:00
Nathaniel Hill
75b3fb4981 [now-python] Pass the request body to the HTTP request (#99)
* pass body to lambda

* reformat

* look for requirements.txt in lambda root

* single issue PR

* support base64 encoded request body

* support local requirements.txt

* formatting cleanup

* Update packages/now-python/now_handler.py

Co-Authored-By: NathanielHill <nata@goguna.com>

* Avoid error on empty POST body

Just realized I forgot to address the empty POST body error. This should do it

* quick formatting fix

remove double quotes for consistency
2019-02-19 11:28:21 -08:00
Steven
49e63de5fe Publish
- @now/next@0.0.85-canary.5
 - @now/node-server@0.4.27-canary.5
 - @now/node@0.4.29-canary.5
2019-02-17 19:09:27 -05:00
Steven
4742cd32f2 [now-node] add sourceMap flag to ncc (#222)
This adds a {sourceMap:true} flag to ncc builds
2019-02-17 19:08:19 -05:00
Steven
377b73105d [now-node] bump ncc to 0.15.2 (#221)
This has a couple fixes.

See release notes here https://github.com/zeit/ncc/releases/tag/0.15.2
2019-02-17 23:55:55 +01:00
Connor Davis
a5577efb3d Prepublish @now/next 2019-02-15 17:07:14 -06:00
Steven
2ec46dc5c9 Publish
- @now/node-server@0.4.27-canary.4
 - @now/node@0.4.29-canary.4
 - @now/rust@0.0.3-canary.2
2019-02-15 12:24:06 -05:00
Steven
42708ed93c [now-node] bump ncc to 0.15.1 (#219)
This fixes a regression introduced in ncc@0.13.2 and also prints the version of ncc used at build time.

See [release notes](https://github.com/zeit/ncc/releases/tag/0.15.1) for more info.
2019-02-15 17:48:58 +01:00
Antonio Nuno Monteiro
2fabe95f6e [now-rust] Build .rs entrypoints (#213) 2019-02-15 15:10:51 +00:00
Nathan Rajlich
ac1a3dab22 Publish
- @now/build-utils@0.4.35-canary.2
 - @now/go@0.2.13-canary.1
 - @now/rust@0.0.3-canary.1
2019-02-14 17:45:31 -08:00
Nathan Rajlich
ad4011512d [now-go] Refactor to be platform-aware for now dev (#216)
* [now-go] Refactor to be platform-aware for `now dev`

This is mostly to have `now dev` work, but also just a general cleanup
of the builder codebase.

Previously, the `get-exported-function-name` Go binary was being
compiled at publish-time for "linux x64" architecture, since that
is what AWS Lambda expects.

In order for `@now/go` to work on MacOS (or any other platform not
compatible with "linux x64") the helper program must be compiled by the
actual host machine, so that it builds a MacOS binary for example.
So building `get-exported-function-name` has been moved from the
"publish" step to the "postinstall" step.

Similarly, the `go` binary itself used to be downloaded explicitly by
the `build()` function during build-time. This step has been moved to
the "postinstall" script as well, so that said binary may be compiled.

This also makes sense because the `@now/go` builder will be downloaded
by `now dev` once, so this way the `go` binary only needs to be
downloaded once as well, rather than once per build.

Any assumptions about `GOOS` and `GOARCH` have also been removed.

* Don't lint the `go` tarball directory

* Use GitHub URL for the go bridge

* Tweak comments

* Use `glob()` instead of `FileFsRef`

`FileFsRef` appears to lose executable permissions on the file,
whereas `glob()` works as expected.
2019-02-14 17:36:33 -08:00
Nathan Rajlich
9ff1a25c8f [now-build-utils] Add FsFileRef.fromFsPath() helper function (#217)
* [now-build-utils] Add `FsFileRef.fromFile()` helper function

This helper function is very similar to the raw `FsFileRef` constructor,
however it stats the file to retrieve the proper `mode`, so that
settings like executable bits are not lost.

* Rename to `FileFsRef.fromFsPath()`
2019-02-14 17:09:01 -08:00
Antonio Nuno Monteiro
8039b3d377 [now-rust] Publish 0.1.2 to crates.io (#215) 2019-02-14 15:03:14 -08:00
Connor Davis
dd9017475c Publish
- @now/lambda@0.4.10-canary.1
 - @now/md@0.4.10-canary.1
 - @now/mdx-deck@0.4.19-canary.1
 - @now/next@0.0.85-canary.4
 - @now/node-server@0.4.27-canary.3
 - @now/node@0.4.29-canary.3
 - @now/php@0.4.14-canary.1
 - @now/static-build@0.4.19-canary.1
2019-02-12 21:41:52 -06:00
Connor Davis
031499014f Give tests 4 minutes instead of 2 2019-02-12 21:40:51 -06:00
Connor Davis
2a68d2a2ad Publish
- @now/build-utils@0.4.35-canary.1
 - @now/next@0.0.85-canary.3
 - @now/rust@0.0.3-canary.0
2019-02-12 21:00:19 -06:00
Connor Davis
31299fae6e Force canary bump in @now/next 2019-02-12 20:59:42 -06:00
Connor Davis
4bac0db379 fix @now/build-utils publishing tmp/ 2019-02-12 20:35:12 -06:00
Connor Davis
95e7d459d3 Increase timeout on now-build-utils 2019-02-12 19:39:15 -06:00
Antonio Nuno Monteiro
dd120b8d20 [now-rist] Implement simpler API for *.rs entrypoints (#210) 2019-02-12 13:21:03 -08:00
Mike Engel
b6975676e5 [now-rust] Import Deserialize traits behind de and ser (#203)
See https://github.com/serde-rs/serde/issues/1441
2019-02-12 13:16:14 -08:00
Igor Klopov
a7951dae81 [tests] produce public deployments 2019-02-12 22:07:10 +03:00
Connor Davis
b0c918f7fb Publish
- @now/next@0.0.85-canary.2
2019-02-11 20:28:52 -06:00
Connor Davis
df54dc7dc9 Download Entire Directory when Building now-next (#200) 2019-02-11 20:23:26 -06:00
paulogdm
0dd801ff6c Update now-next-legacy-mode.md (#208)
* Update now-next-legacy-mode.md

From `canary` to `latest`.

* Update now-next-no-serverless-pages-built.md

From `canary` to `latest`

* Apply suggestions from code review

Better words...

Co-Authored-By: paulogdm <paulogdemitri@gmail.com>
2019-02-11 14:41:42 +01:00
Connor Davis
398743ef95 Fix tests not completing 2019-02-08 20:03:32 -06:00
Nathan Rajlich
337c74b81b Publish
- @now/bash@0.1.2
2019-02-08 15:28:30 -08:00
Nathan Rajlich
680bb82ec3 [now-bash] Use mktemp dry run syntax that is compatible with BSD (#204)
For `now dev` running on a MacOS machine, some of our builders will need
to be adjusted to be "platform-aware".

In this case, `mktemp` is provided by BSD instead of GNU, which doesn't
support the long form `--dry-run` flag, however both support the `-u`
flag which does the same thing.
2019-02-08 15:25:51 -08:00
Nathan Rajlich
17ed5411e3 Publish
- @now/rust@0.0.2
2019-02-06 12:58:51 -08:00
Nathan Rajlich
d9bbcb6939 Publish
- @now/rust@0.0.2-canary.2
 - @now/static-build@0.4.19-canary.0
2019-02-05 12:06:03 -08:00
Igor Klopov
800e4de76f [tests] lint fixes 2019-02-05 22:35:18 +03:00
António Nuno Monteiro
864dd468d9 Place generated @now/rust lambdas in the location that matches their entrypoint (#198) 2019-02-05 11:31:41 -08:00
Igor Klopov
ba833871bb [tests] merge build.env from now.json 2019-02-05 22:15:02 +03:00
Arunoda Susiripal
e732bac78e @now/static-build@0.4.18 2019-02-05 02:27:26 +00:00
Arunoda Susiripal
28ea4015b4 Publish
- @now/static-build@0.4.18-canary.3
2019-02-05 02:12:15 +00:00
Arunoda Susiripal
a93d97cabd Use proper test code for now-static-build 2019-02-05 02:10:25 +00:00
Arunoda Susiripal
67f39f7c9b Publish
- @now/static-build@0.4.18-canary.2
2019-02-05 01:58:26 +00:00
Arunoda Susiripal
acd793b9e9 Fix a typo in a message of now-static-build 2019-02-05 01:57:27 +00:00
Arunoda Susiripal
f74d61279d Publish
- @now/static-build@0.4.18-canary.1
2019-02-05 01:50:45 +00:00
Arunoda Susiripala
fcb8eacec0 Check for distDir existence inside now-static-build (#197)
* Throw in case we don't see the distDir

* Add test cases.
2019-02-05 07:18:30 +05:30
Nathan Rajlich
c8fca2ba72 Publish
- @now/build-utils@0.4.35-canary.0
 - @now/rust@0.0.2-canary.1
2019-02-04 14:44:28 -08:00
António Nuno Monteiro
4feffa13eb Fix @now/rust for nested paths (#194)
* Fix `@now/rust` for nested paths

* fix bug

* fs-extra

* no need for mkdirp or rimraf anymore
2019-02-04 14:40:41 -08:00
Igor Klopov
3e330b25f4 Publish 2019-02-04 21:38:37 +03:00
Igor Klopov
9b2cae33af [build-utils] removed in-memory yarn cache 2019-02-04 19:45:46 +03:00
António Nuno Monteiro
4b6371530c Publish initial version to crates.io (#193) 2019-02-01 00:11:45 +01:00
Nathan Rajlich
9e1d577fc0 Publish
- @now/bash@0.1.2-canary.0
 - @now/build-utils@0.4.33-canary.2
 - @now/cgi@0.0.16-canary.0
 - @now/go@0.2.13-canary.0
 - @now/html-minifier@1.0.8-canary.0
 - @now/lambda@0.4.10-canary.0
 - @now/md@0.4.10-canary.0
 - @now/mdx-deck@0.4.19-canary.0
 - @now/next@0.0.85-canary.1
 - @now/node-bridge@0.1.11-canary.0
 - @now/node-server@0.4.27-canary.2
 - @now/node@0.4.29-canary.2
 - @now/optipng@0.4.9-canary.0
 - @now/php-bridge@0.4.14-canary.0
 - @now/php@0.4.14-canary.0
 - @now/python@0.0.41-canary.0
 - @now/rust@0.0.2-canary.0
 - @now/static-build@0.4.18-canary.0
 - @now/wordpress@0.4.15-canary.0
2019-01-31 14:32:48 -08:00
António Nuno Monteiro
cf2f542c71 Initial import of @now/rust (#191)
* Fix docstrings, remove unused rust dev dependency

* Add support for multiple binaries, prepareCache too

* Remove context from the handler signature, abstract over the AWS error

* Fix Rust tarball URL, caching and lint

* GCC URL
2019-01-31 22:29:50 +00:00
Igor Klopov
e608861e4e [now-node] bump ncc 2019-01-31 02:52:37 +03:00
Steven
a99b999209 Remove @now/build-utils peer dependency (#187)
* Remove peer dependencies

* Suppress eslint warning
2019-01-29 15:18:36 -05:00
Steven
fd9c6e7847 Add license to satisify warning (#186)
This solves the warning seen in `/_logs` all the time:

```
warning package.json: No license field
```
2019-01-29 12:59:25 -05:00
Steven
b2ad3a6147 Add repository key to package.json (#185) 2019-01-29 17:52:01 +01:00
Steven
997d3c2a30 Add link to builder docs (#184) 2019-01-29 17:24:54 +01:00
Igor Klopov
ca575bf0a6 Publish
- @now/next@0.0.85-canary.0
 - @now/node-server@0.4.27-canary.1
 - @now/node@0.4.29-canary.1
2019-01-29 19:09:01 +03:00
Igor Klopov
4c2e93ccef [now-node] bump ncc 2019-01-29 18:28:13 +03:00
paulogdm
4d6437d235 Fix broken error link (#175)
* Fix broken error link

* Added legacy mode error

Standard "Next.js" for all citations of Next.
Add "Legacy Mode" error.

* Update no serverless pages built doc

* Update doc on now/next legacy mode

* Add link after the error message
2019-01-28 19:05:02 +01:00
Igor Klopov
0d8058d062 Publish
- @now/node-server@0.4.27-canary.0
 - @now/node@0.4.29-canary.0
2019-01-27 21:58:47 +03:00
Igor Klopov
2b5cdfc0a7 [now-node] bump ncc 2019-01-27 15:27:00 +03:00
Igor Klopov
69a41f78fb Publish
- @now/build-utils@0.4.33-canary.1
2019-01-26 00:59:33 +03:00
Igor Klopov
a013d59d62 [build-utils] yarn in linux calls chown. mock it as well 2019-01-26 00:11:09 +03:00
Igor Klopov
173a29cfdb Publish
- @now/build-utils@0.4.33-canary.0
2019-01-25 02:51:46 +03:00
Igor Klopov
3f73451311 [build-utils] lint fixes 2019-01-25 00:55:32 +03:00
Igor Klopov
2fc706be43 [build-utils] make in-memory cache support git deps 2019-01-25 00:41:33 +03:00
Igor Klopov
0fb7eb6093 [build-utils] don't print download time of individual files 2019-01-24 18:44:10 +03:00
Igor Klopov
aa43c0bc87 [tests] consider now's 404 a case to retry 2019-01-23 18:43:55 +03:00
Igor Klopov
3c5925a6e3 [tests] print what failed page contains 2019-01-23 01:14:01 +03:00
Igor Klopov
9fc7b047f5 [tests] buildDelegate argument for testDeployment 2019-01-23 00:14:43 +03:00
Igor Klopov
ecae29457f Revert "[lerna] trying to set the order of package to publish"
This reverts commit 79251ad180.
2019-01-20 03:35:23 +03:00
103 changed files with 4169 additions and 2230 deletions

12
.circleci/build.sh Executable file
View File

@@ -0,0 +1,12 @@
#!/bin/bash
set -euo pipefail
circleci_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
needs_build="$(grep -rn '"build"' packages/*/package.json | cut -d: -f1)"
for pkg in $needs_build; do
dir="$(dirname "$pkg")"
cd "$circleci_dir/../$dir"
echo "Building \`$dir\`"
yarn build
done

View File

@@ -23,6 +23,9 @@ jobs:
- run:
name: Linting
command: yarn lint
- run:
name: Building
command: ./.circleci/build.sh
- run:
name: Tests
command: yarn test
@@ -30,11 +33,8 @@ jobs:
name: Potentially save npm token
command: "([[ ! -z $NPM_TOKEN ]] && echo \"//registry.npmjs.org/:_authToken=$NPM_TOKEN\" >> ~/.npmrc) || echo \"Did not write npm token\""
- run:
name: Potentially publish canary release
command: "if ls ~/.npmrc >/dev/null 2>&1 && [[ $(git describe --exact-match 2> /dev/null || :) =~ -canary ]]; then yarn run lerna publish from-git --npm-tag canary --yes; else echo \"Did not publish\"; fi"
- run:
name: Potentially publish stable release
command: "if ls ~/.npmrc >/dev/null 2>&1 && [[ ! $(git describe --exact-match 2> /dev/null || :) =~ -canary ]]; then yarn run lerna publish from-git --yes; else echo \"Did not publish\"; fi"
name: Potentially publish releases to npm
command: ./.circleci/publish.sh
workflows:
version: 2
build-and-deploy:

24
.circleci/publish.sh Executable file
View File

@@ -0,0 +1,24 @@
#!/bin/bash
set -euo pipefail
if [ ! -e ~/.npmrc ]; then
echo "~/.npmrc file does not exist, skipping publish"
exit 0
fi
npm_tag=""
tag="$(git describe --exact-match 2> /dev/null || :)"
if [ -z "$tag" ]; then
echo "Not a tagged commit, skipping publish"
exit 0
fi
if [[ "$tag" =~ -canary ]]; then
echo "Publishing canary release"
npm_tag="--npm-tag canary"
else
echo "Publishing stable release"
fi
yarn run lerna publish from-git $npm_tag --yes

View File

@@ -1,3 +1,4 @@
/tmp/*
/node_modules/*
/**/node_modules/*
/packages/now-go/go/*

2
.gitignore vendored
View File

@@ -1,2 +1,4 @@
node_modules
tmp
target/
.next

View File

@@ -2,7 +2,7 @@
This is the full list of official Builders provided by the ZEIT team.
More details here: http://zeit.co/docs
More details here: https://zeit.co/docs/v2/deployments/builders/overview/
### Publishing to npm

View File

@@ -0,0 +1,73 @@
# `@now/next` Legacy Mode
#### Why This Warning Occurred
`@now/next` has two modes: `legacy` and `serverless`. You will always want to use the `serverless` mode. `legacy` is to provide backwards compatibility with previous `@now/next` versions.
The differences:
Legacy:
- Minimal lambda size of `2.2Mb` (approximately)
- Forces `next@v7.0.2-canary.49` and `next-server@v7.0.2-canary.49`
- Forces all `dependencies` to be `devDependencies`
- Loads `next.config.js` on bootup, breaking sometimes when users didn't use `phases` to load files
- Used `next-server` which is the full Next.js server with routing etc.
- Runs `npm install`
- Runs `npm run now-build`
- Runs `npm install --production` after build
Serverless:
- Minimal lambda size of `49Kb` (approximately)
- Uses Next.js build targets (`target: 'serverless'`) in `next.config.js`. [documentation](https://github.com/zeit/next.js#summary)
- Does not make changes to your application dependencies
- Does not load `next.config.js` ([as per the serverless target documentation](https://github.com/zeit/next.js#summary))
- Runs `npm install`
- Runs `npm run now-build`
- Does not run `npm install --production` as the output from the build is all that's needed to bundle lambdas.
- No runtime dependencies, meaning smaller lambda functions
- Optimized for fast [cold start](https://zeit.co/blog/serverless-ssr#cold-start)
#### Possible Ways to Fix It
In order to create the smallest possible lambdas Next.js has to be configured to build for the `serverless` target.
1. Serverless Next.js requires Next.js 8 or later, to upgrade you can install the `latest` version:
```
npm install next --save
```
2. Add the `now-build` script to your `package.json`
```json
{
"scripts": {
"now-build": "next build"
},
}
```
3. Add `target: 'serverless'` to `next.config.js`
```js
module.exports = {
target: 'serverless'
// Other options are still valid
}
```
4. Optionally make sure the `"src"` in `"builds"` points to your application `package.json`
```js
{
"version": 2,
"builds": [{ "src": "package.json", "use": "@now/next" }]
}
```
### Useful Links
- [Serverless target implementation](https://github.com/zeit/now-builders/pull/150)

View File

@@ -0,0 +1,43 @@
# `@now/next` No Serverless Pages Built
#### Why This Error Occurred
This error occurs when you have your application is not configured for Serverless Next.js build output.
#### Possible Ways to Fix It
In order to create the smallest possible lambdas Next.js has to be configured to build for the `serverless` target.
1. Serverless Next.js requires Next.js 8 or later, to upgrade you can install the `latest` version:
```
npm install next --save
```
2. Add the `now-build` script to your `package.json`
```json
{
"scripts": {
"now-build": "next build"
},
}
```
3. Add `target: 'serverless'` to `next.config.js`
```js
module.exports = {
target: 'serverless'
// Other options are still valid
}
```
4. Optionally make sure the `"src"` in `"builds"` points to your application `package.json`
```js
{
"version": 2,
"builds": [{ "src": "package.json", "use": "@now/next" }]
}
```

View File

@@ -2,24 +2,7 @@
"npmClient": "yarn",
"useWorkspaces": true,
"packages": [
"packages/now-build-utils",
"packages/now-node-bridge",
"packages/now-php-bridge",
"packages/now-bash",
"packages/now-cgi",
"packages/now-go",
"packages/now-html-minifier",
"packages/now-lambda",
"packages/now-md",
"packages/now-mdx-deck",
"packages/now-next",
"packages/now-node",
"packages/now-node-server",
"packages/now-optipng",
"packages/now-php",
"packages/now-python",
"packages/now-static-build",
"packages/now-wordpress"
"packages/*"
],
"command": {
"publish": {

View File

@@ -15,7 +15,7 @@
"publish-stable": "lerna version",
"publish-canary": "lerna version prerelease --preid canary",
"lint": "tsc && eslint .",
"test": "jest --runInBand",
"test": "jest --runInBand --verbose",
"lint-staged": "lint-staged"
},
"pre-commit": "lint-staged",

View File

@@ -1,10 +1,10 @@
const execa = require('execa');
const { join } = require('path');
const snakeCase = require('snake-case');
const glob = require('@now/build-utils/fs/glob');
const download = require('@now/build-utils/fs/download');
const { createLambda } = require('@now/build-utils/lambda');
const getWritableDirectory = require('@now/build-utils/fs/get-writable-directory');
const glob = require('@now/build-utils/fs/glob'); // eslint-disable-line import/no-extraneous-dependencies
const download = require('@now/build-utils/fs/download'); // eslint-disable-line import/no-extraneous-dependencies
const { createLambda } = require('@now/build-utils/lambda'); // eslint-disable-line import/no-extraneous-dependencies
const getWritableDirectory = require('@now/build-utils/fs/get-writable-directory'); // eslint-disable-line import/no-extraneous-dependencies
exports.config = {
maxLambdaSize: '10mb',

View File

@@ -1,10 +1,15 @@
{
"name": "@now/bash",
"version": "0.1.1",
"version": "0.1.2",
"description": "Now 2.0 builder for HTTP endpoints written in Bash",
"main": "index.js",
"author": "Nathan Rajlich <nate@zeit.co>",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/zeit/now-builders.git",
"directory": "packages/now-bash"
},
"files": [
"builder.sh",
"runtime.sh",
@@ -15,8 +20,5 @@
"dependencies": {
"execa": "^1.0.0",
"snake-case": "^2.1.0"
},
"peerDependencies": {
"@now/build-utils": ">=0.0.1"
}
}

View File

@@ -52,7 +52,7 @@ _lambda_runtime_next() {
# Need to use a fifo here instead of bash <() because Lambda
# errors with "/dev/fd/63 not found" for some reason :/
local stdin
stdin="$(mktemp --dry-run)"
stdin="$(mktemp -u)"
mkfifo "$stdin"
_lambda_runtime_body "$event" > "$stdin" &

View File

@@ -1 +1,2 @@
/test
tmp

View File

@@ -26,6 +26,18 @@ class FileFsRef {
this.fsPath = fsPath;
}
/**
* Creates a `FileFsRef` with the correct `mode` from the file system.
*
* @argument {Object} options
* @argument {string} options.fsPath
* @returns {Promise<FileFsRef>}
*/
static async fromFsPath({ fsPath }) {
const { mode } = await fs.lstat(fsPath);
return new FileFsRef({ mode, fsPath });
}
/**
* @argument {Object} options
* @argument {number} [options.mode=0o100644]

View File

@@ -49,7 +49,7 @@ class FileRef {
assert(url);
await semaToDownloadFromS3.acquire();
console.time(`downloading ${url}`);
// console.time(`downloading ${url}`);
try {
return await retry(
async () => {
@@ -66,7 +66,7 @@ class FileRef {
{ factor: 1, retries: 3 },
);
} finally {
console.timeEnd(`downloading ${url}`);
// console.timeEnd(`downloading ${url}`);
semaToDownloadFromS3.release();
}
}

View File

@@ -1,94 +0,0 @@
const MemoryFileSystem = require('memory-fs');
const fs = require('fs');
const path = require('path');
const { spawnSync } = require('child_process');
const yarnPath = spawnSync('which', ['yarn'])
.stdout.toString()
.trim();
const cachePath = spawnSync(yarnPath, ['cache', 'dir'])
.stdout.toString()
.trim();
spawnSync(yarnPath, ['cache', 'clean']);
const vfs = new MemoryFileSystem();
function isInsideCachePath(filename) {
const relative = path.relative(cachePath, filename);
return !relative.startsWith('..');
}
const saveCreateWriteStream = fs.createWriteStream;
fs.createWriteStream = (...args) => {
const filename = args[0];
if (!isInsideCachePath(filename)) {
return saveCreateWriteStream.call(fs, ...args);
}
vfs.mkdirpSync(path.dirname(filename));
fs.writeFileSync(filename, Buffer.alloc(0));
const stream = vfs.createWriteStream(...args);
stream.on('finish', () => {
setTimeout(() => {
stream.emit('close');
});
});
return stream;
};
const saveReadFile = fs.readFile;
fs.readFile = (...args) => {
const filename = args[0];
if (!isInsideCachePath(filename)) {
return saveReadFile.call(fs, ...args);
}
const callback = args[args.length - 1];
return vfs.readFile(...args.slice(0, -1), (error, result) => {
if (error) {
saveReadFile.call(fs, ...args);
return;
}
callback(error, result);
});
};
const saveCopyFile = fs.copyFile;
fs.copyFile = (...args) => {
const src = args[0];
const dest = args[1];
const callback = args[args.length - 1];
if (isInsideCachePath(src) && !isInsideCachePath(dest)) {
const buffer = vfs.readFileSync(src);
return fs.writeFile(dest, buffer, callback);
}
if (!isInsideCachePath(src) && isInsideCachePath(dest)) {
const buffer = fs.readFileSync(src);
vfs.mkdirpSync(path.dirname(dest));
fs.writeFileSync(dest, Buffer.alloc(0));
return vfs.writeFile(dest, buffer, callback);
}
return saveCopyFile.call(fs, ...args);
};
const saveWriteFile = fs.writeFile;
fs.writeFile = (...args) => {
const filename = args[0];
if (!isInsideCachePath(filename)) {
return saveWriteFile.call(fs, ...args);
}
vfs.mkdirpSync(path.dirname(filename));
fs.writeFileSync(filename, Buffer.alloc(0));
return vfs.writeFile(...args);
};
require(yarnPath);

View File

@@ -3,9 +3,6 @@ const fs = require('fs-extra');
const path = require('path');
const { spawn } = require('child_process');
const prod = process.env.AWS_EXECUTION_ENV
|| process.env.X_GOOGLE_CODE_LOCATION;
function spawnAsync(command, args, cwd) {
return new Promise((resolve, reject) => {
const child = spawn(command, args, { stdio: 'inherit', cwd });
@@ -66,15 +63,6 @@ async function installDependencies(destPath, args = []) {
commandArgs = args.filter(a => a !== '--prefer-offline');
await spawnAsync('npm', ['install'].concat(commandArgs), destPath);
await spawnAsync('npm', ['cache', 'clean', '--force'], destPath);
} else if (prod) {
console.log('using memory-fs for yarn cache');
await spawnAsync(
'node',
[path.join(__dirname, 'bootstrap-yarn.js'), '--cwd', destPath].concat(
commandArgs,
),
destPath,
);
} else {
await spawnAsync('yarn', ['--cwd', destPath].concat(commandArgs), destPath);
await spawnAsync('yarn', ['cache', 'clean'], destPath);

View File

@@ -1,7 +1,12 @@
{
"name": "@now/build-utils",
"version": "0.4.32",
"version": "0.4.35-canary.2",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/zeit/now-builders.git",
"directory": "packages/now-build-utils"
},
"dependencies": {
"async-retry": "1.2.3",
"async-sema": "2.1.4",

View File

@@ -7,7 +7,7 @@ const {
testDeployment,
} = require('../../../test/lib/deployment/test-deployment.js');
jest.setTimeout(2 * 60 * 1000);
jest.setTimeout(4 * 60 * 1000);
const builderUrl = '@canary';
let buildUtilsUrl;

View File

@@ -1,10 +1,10 @@
const path = require('path');
const { mkdirp, copyFile } = require('fs-extra');
const glob = require('@now/build-utils/fs/glob');
const download = require('@now/build-utils/fs/download');
const { createLambda } = require('@now/build-utils/lambda');
const getWritableDirectory = require('@now/build-utils/fs/get-writable-directory');
const glob = require('@now/build-utils/fs/glob'); // eslint-disable-line import/no-extraneous-dependencies
const download = require('@now/build-utils/fs/download'); // eslint-disable-line import/no-extraneous-dependencies
const { createLambda } = require('@now/build-utils/lambda'); // eslint-disable-line import/no-extraneous-dependencies
const getWritableDirectory = require('@now/build-utils/fs/get-writable-directory'); // eslint-disable-line import/no-extraneous-dependencies
exports.analyze = ({ files, entrypoint }) => files[entrypoint].digest;

View File

@@ -1,7 +1,12 @@
{
"name": "@now/cgi",
"version": "0.0.15",
"version": "0.0.16-canary.0",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/zeit/now-builders.git",
"directory": "packages/now-cgi"
},
"scripts": {
"test": "best -I test/*.js",
"prepublish": "./build.sh"
@@ -16,8 +21,5 @@
"devDependencies": {
"@zeit/best": "0.4.3",
"rmfr": "2.0.0"
},
"peerDependencies": {
"@now/build-utils": ">=0.0.1"
}
}

View File

@@ -1,4 +1,5 @@
node_modules
*.log
launcher
bin
/?.js
/go
/get-exported-function-name

View File

@@ -1,7 +0,0 @@
#!/usr/bin/env bash
mkdir -p bin
cd util
GOOS=linux GOARCH=amd64 go build get-exported-function-name.go
mv get-exported-function-name ../bin/

View File

@@ -1,23 +0,0 @@
const path = require('path');
const fetch = require('node-fetch');
const tar = require('tar');
const getWritableDirectory = require('@now/build-utils/fs/get-writable-directory.js');
const url = 'https://dl.google.com/go/go1.11.1.linux-amd64.tar.gz';
module.exports = async () => {
const res = await fetch(url);
const dir = await getWritableDirectory();
if (!res.ok) {
throw new Error(`Failed to download: ${url}`);
}
return new Promise((resolve, reject) => {
res.body
.on('error', reject)
.pipe(tar.extract({ cwd: dir, strip: 1 }))
.on('finish', () => resolve(path.join(dir, 'bin', 'go')));
});
};

View File

@@ -0,0 +1,124 @@
const tar = require('tar');
const execa = require('execa');
const fetch = require('node-fetch');
const { mkdirp } = require('fs-extra');
const { dirname, join } = require('path');
const debug = require('debug')('@now/go:go-helpers');
const archMap = new Map([['x64', 'amd64'], ['x86', '386']]);
const platformMap = new Map([['win32', 'windows']]);
// Location where the `go` binary will be installed after `postinstall`
const GO_DIR = join(__dirname, 'go');
const GO_BIN = join(GO_DIR, 'bin/go');
const getPlatform = p => platformMap.get(p) || p;
const getArch = a => archMap.get(a) || a;
const getGoUrl = (version, platform, arch) => {
const goArch = getArch(arch);
const goPlatform = getPlatform(platform);
const ext = platform === 'win32' ? 'zip' : 'tar.gz';
return `https://dl.google.com/go/go${version}.${goPlatform}-${goArch}.${ext}`;
};
function getExportedFunctionName(filePath) {
debug('Detecting handler name for %o', filePath);
const bin = join(__dirname, 'get-exported-function-name');
const args = [filePath];
const name = execa.stdout(bin, args);
debug('Detected exported name %o', filePath);
return name;
}
// Creates a `$GOPATH` directory tree, as per `go help gopath` instructions.
// Without this, `go` won't recognize the `$GOPATH`.
function createGoPathTree(goPath, platform, arch) {
const tuple = `${getPlatform(platform)}_${getArch(arch)}`;
debug('Creating GOPATH directory structure for %o (%s)', goPath, tuple);
return Promise.all([
mkdirp(join(goPath, 'bin')),
mkdirp(join(goPath, 'pkg', tuple)),
]);
}
async function get({ src } = {}) {
const args = ['get'];
if (src) {
debug('Fetching `go` dependencies for file %o', src);
args.push(src);
} else {
debug('Fetching `go` dependencies for cwd %o', this.cwd);
}
await this(...args);
}
async function build({ src, dest }) {
debug('Building `go` binary %o -> %o', src, dest);
let sources;
if (Array.isArray(src)) {
sources = src;
} else {
sources = [src];
}
await this('build', '-o', dest, ...sources);
}
async function createGo(
goPath,
platform = process.platform,
arch = process.arch,
opts = {},
) {
const env = {
...process.env,
PATH: `${dirname(GO_BIN)}:${process.env.PATH}`,
GOPATH: goPath,
...opts.env,
};
function go(...args) {
debug('Exec %o', `go ${args.join(' ')}`);
return execa('go', args, { stdio: 'inherit', ...opts, env });
}
go.cwd = opts.cwd || process.cwd();
go.get = get;
go.build = build;
go.goPath = goPath;
await createGoPathTree(goPath, platform, arch);
return go;
}
async function downloadGo(
dir = GO_DIR,
version = '1.11.5',
platform = process.platform,
arch = process.arch,
) {
debug('Installing `go` v%s to %o for %s %s', version, dir, platform, arch);
const url = getGoUrl(version, platform, arch);
debug('Downloading `go` URL: %o', url);
const res = await fetch(url);
if (!res.ok) {
throw new Error(`Failed to download: ${url} (${res.status})`);
}
// TODO: use a zip extractor when `ext === "zip"`
await mkdirp(dir);
await new Promise((resolve, reject) => {
res.body
.on('error', reject)
.pipe(tar.extract({ cwd: dir, strip: 1 }))
.on('error', reject)
.on('finish', resolve);
});
return createGo(dir, platform, arch);
}
module.exports = {
createGo,
downloadGo,
getExportedFunctionName,
};

View File

@@ -1,79 +1,52 @@
const path = require('path');
const { mkdirp, readFile, writeFile } = require('fs-extra');
const { join, dirname } = require('path');
const { readFile, writeFile } = require('fs-extra');
const execa = require('execa');
const { createLambda } = require('@now/build-utils/lambda.js');
const getWritableDirectory = require('@now/build-utils/fs/get-writable-directory.js');
const download = require('@now/build-utils/fs/download.js');
const downloadGit = require('lambda-git');
const glob = require('@now/build-utils/fs/glob.js');
const downloadGoBin = require('./download-go-bin');
const glob = require('@now/build-utils/fs/glob.js'); // eslint-disable-line import/no-extraneous-dependencies
const download = require('@now/build-utils/fs/download.js'); // eslint-disable-line import/no-extraneous-dependencies
const { createLambda } = require('@now/build-utils/lambda.js'); // eslint-disable-line import/no-extraneous-dependencies
const getWritableDirectory = require('@now/build-utils/fs/get-writable-directory.js'); // eslint-disable-line import/no-extraneous-dependencies
const { createGo, getExportedFunctionName } = require('./go-helpers');
// creates a `$GOPATH` directory tree, as per
// `go help gopath`'s instructions.
// without this, Go won't recognize the `$GOPATH`
async function createGoPathTree(goPath) {
await mkdirp(path.join(goPath, 'bin'));
await mkdirp(path.join(goPath, 'pkg', 'linux_amd64'));
}
exports.config = {
const config = {
maxLambdaSize: '10mb',
};
exports.build = async ({ files, entrypoint }) => {
console.log('downloading files...');
async function build({ files, entrypoint }) {
console.log('Downloading user files...');
const gitPath = await getWritableDirectory();
const goPath = await getWritableDirectory();
const srcPath = path.join(goPath, 'src', 'lambda');
const outDir = await getWritableDirectory();
await createGoPathTree(goPath);
const [goPath, outDir] = await Promise.all([
getWritableDirectory(),
getWritableDirectory(),
]);
const srcPath = join(goPath, 'src', 'lambda');
const downloadedFiles = await download(files, srcPath);
console.log('downloading go binary...');
const goBin = await downloadGoBin();
console.log('downloading git binary...');
// downloads a git binary that works on Amazon Linux and sets
// `process.env.GIT_EXEC_PATH` so `go(1)` can see it
await downloadGit({ targetDirectory: gitPath });
const goEnv = {
...process.env,
GOOS: 'linux',
GOARCH: 'amd64',
GOPATH: goPath,
};
console.log(`parsing AST for "${entrypoint}"`);
let handlerFunctionName = '';
console.log(`Parsing AST for "${entrypoint}"`);
let handlerFunctionName;
try {
handlerFunctionName = await execa.stdout(
path.join(__dirname, 'bin', 'get-exported-function-name'),
[downloadedFiles[entrypoint].fsPath],
handlerFunctionName = await getExportedFunctionName(
downloadedFiles[entrypoint].fsPath,
);
} catch (err) {
console.log(`failed to parse AST for "${entrypoint}"`);
console.log(`Failed to parse AST for "${entrypoint}"`);
throw err;
}
if (handlerFunctionName === '') {
const e = new Error(
`Could not find an exported function on "${entrypoint}"`,
if (!handlerFunctionName) {
const err = new Error(
`Could not find an exported function in "${entrypoint}"`,
);
console.log(e.message);
throw e;
console.log(err.message);
throw err;
}
console.log(
`Found exported function "${handlerFunctionName}" on "${entrypoint}"`,
`Found exported function "${handlerFunctionName}" in "${entrypoint}"`,
);
const origianlMainGoContents = await readFile(
path.join(__dirname, 'main.go'),
join(__dirname, 'main.go'),
'utf8',
);
const mainGoContents = origianlMainGoContents.replace(
@@ -85,39 +58,33 @@ exports.build = async ({ files, entrypoint }) => {
// we need `main.go` in the same dir as the entrypoint,
// otherwise `go build` will refuse to build
const entrypointDirname = path.dirname(downloadedFiles[entrypoint].fsPath);
const entrypointDirname = dirname(downloadedFiles[entrypoint].fsPath);
// Go doesn't like to build files in different directories,
// so now we place `main.go` together with the user code
await writeFile(path.join(entrypointDirname, mainGoFileName), mainGoContents);
await writeFile(join(entrypointDirname, mainGoFileName), mainGoContents);
console.log('installing dependencies');
// `go get` will look at `*.go` (note we set `cwd`), parse
// the `import`s and download any packages that aren't part of the stdlib
const go = await createGo(goPath, process.platform, process.arch, {
cwd: entrypointDirname,
});
// `go get` will look at `*.go` (note we set `cwd`), parse the `import`s
// and download any packages that aren't part of the stdlib
try {
await execa(goBin, ['get'], {
env: goEnv,
cwd: entrypointDirname,
stdio: 'inherit',
});
await go.get();
} catch (err) {
console.log('failed to `go get`');
throw err;
}
console.log('running go build...');
console.log('Running `go build`...');
const destPath = join(outDir, 'handler');
try {
await execa(
goBin,
[
'build',
'-o',
path.join(outDir, 'handler'),
path.join(entrypointDirname, mainGoFileName),
downloadedFiles[entrypoint].fsPath,
],
{ env: goEnv, cwd: entrypointDirname, stdio: 'inherit' },
);
const src = [
join(entrypointDirname, mainGoFileName),
downloadedFiles[entrypoint].fsPath,
];
await go.build({ src, dest: destPath });
} catch (err) {
console.log('failed to `go build`');
throw err;
@@ -133,4 +100,6 @@ exports.build = async ({ files, entrypoint }) => {
return {
[entrypoint]: lambda,
};
};
}
module.exports = { config, build };

View File

@@ -1,8 +1,8 @@
package main
import (
now "../../utils/go/bridge"
"net/http"
now "github.com/zeit/now-builders/utils/go/bridge"
)
func main() {

View File

@@ -1,30 +1,26 @@
{
"name": "@now/go",
"version": "0.2.12",
"version": "0.2.13-canary.1",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/zeit/now-builders.git",
"directory": "packages/now-go"
},
"scripts": {
"test": "best -I test/*.js",
"prepublish": "./build.sh"
"postinstall": "node ./util/install"
},
"files": [
"bin",
"download-go-bin.js",
"index.js",
"main.go"
"*.js",
"main.go",
"util"
],
"dependencies": {
"debug": "^4.1.1",
"execa": "^1.0.0",
"fs-extra": "^7.0.0",
"lambda-git": "^0.1.2",
"mkdirp-promise": "5.0.1",
"node-fetch": "^2.2.1",
"tar": "4.4.6"
},
"devDependencies": {
"@zeit/best": "0.4.3",
"rmfr": "2.0.0"
},
"peerDependencies": {
"@now/build-utils": ">=0.0.1"
}
}

View File

@@ -0,0 +1,18 @@
const { join } = require('path');
const { downloadGo } = require('../go-helpers');
async function main() {
// First download the `go` binary for this platform/arch.
const go = await downloadGo();
// Build the `get-exported-function-name` helper program.
// `go get` is not necessary because the program has no external deps.
const src = join(__dirname, 'get-exported-function-name.go');
const dest = join(__dirname, '../get-exported-function-name');
await go.build({ src, dest });
}
main().catch((err) => {
console.error(err);
process.exit(1);
});

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
const FileBlob = require('@now/build-utils/file-blob.js');
const FileBlob = require('@now/build-utils/file-blob.js'); // eslint-disable-line import/no-extraneous-dependencies
const { minify } = require('html-minifier');
const defaultOptions = {

View File

@@ -1,11 +1,13 @@
{
"name": "@now/html-minifier",
"version": "1.0.7",
"version": "1.0.8-canary.0",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/zeit/now-builders.git",
"directory": "packages/now-html-minifier"
},
"dependencies": {
"html-minifier": "3.5.21"
},
"peerDependencies": {
"@now/build-utils": ">=0.0.1"
}
}

View File

@@ -1,5 +1,5 @@
const { Lambda } = require('@now/build-utils/lambda.js');
const streamToBuffer = require('@now/build-utils/fs/stream-to-buffer.js');
const { Lambda } = require('@now/build-utils/lambda.js'); // eslint-disable-line import/no-extraneous-dependencies
const streamToBuffer = require('@now/build-utils/fs/stream-to-buffer.js'); // eslint-disable-line import/no-extraneous-dependencies
exports.build = async ({ files, entrypoint, config }) => {
if (!files[entrypoint]) throw new Error('Entrypoint not found in files');

View File

@@ -1,9 +1,11 @@
{
"name": "@now/lambda",
"version": "0.4.9",
"version": "0.4.10-canary.1",
"license": "MIT",
"peerDependencies": {
"@now/build-utils": ">=0.0.1"
"repository": {
"type": "git",
"url": "https://github.com/zeit/now-builders.git",
"directory": "packages/now-lambda"
},
"scripts": {
"test": "jest"

View File

@@ -7,7 +7,7 @@ const {
testDeployment,
} = require('../../../test/lib/deployment/test-deployment.js');
jest.setTimeout(2 * 60 * 1000);
jest.setTimeout(4 * 60 * 1000);
const buildUtilsUrl = '@canary';
let builderUrl;

View File

@@ -1,4 +1,4 @@
const FileBlob = require('@now/build-utils/file-blob.js');
const FileBlob = require('@now/build-utils/file-blob.js'); // eslint-disable-line import/no-extraneous-dependencies
const unified = require('unified');
const unifiedStream = require('unified-stream');
const markdown = require('remark-parse');

View File

@@ -1,7 +1,12 @@
{
"name": "@now/md",
"version": "0.4.9",
"version": "0.4.10-canary.1",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/zeit/now-builders.git",
"directory": "packages/now-md"
},
"dependencies": {
"rehype-document": "^2.2.0",
"rehype-format": "^2.3.0",
@@ -11,9 +16,6 @@
"unified": "^7.0.0",
"unified-stream": "^1.0.2"
},
"peerDependencies": {
"@now/build-utils": ">=0.0.1"
},
"scripts": {
"test": "jest"
}

View File

@@ -7,7 +7,7 @@ const {
testDeployment,
} = require('../../../test/lib/deployment/test-deployment.js');
jest.setTimeout(2 * 60 * 1000);
jest.setTimeout(4 * 60 * 1000);
const buildUtilsUrl = '@canary';
let builderUrl;

View File

@@ -1,10 +1,10 @@
const download = require('@now/build-utils/fs/download.js');
const download = require('@now/build-utils/fs/download.js'); // eslint-disable-line import/no-extraneous-dependencies
const fs = require('fs');
const { promisify } = require('util');
const getWritableDirectory = require('@now/build-utils/fs/get-writable-directory.js');
const glob = require('@now/build-utils/fs/glob.js');
const getWritableDirectory = require('@now/build-utils/fs/get-writable-directory.js'); // eslint-disable-line import/no-extraneous-dependencies
const glob = require('@now/build-utils/fs/glob.js'); // eslint-disable-line import/no-extraneous-dependencies
const path = require('path');
const { runNpmInstall } = require('@now/build-utils/fs/run-user-scripts.js');
const { runNpmInstall } = require('@now/build-utils/fs/run-user-scripts.js'); // eslint-disable-line import/no-extraneous-dependencies
const writeFile = promisify(fs.writeFile);

View File

@@ -1,9 +1,11 @@
{
"name": "@now/mdx-deck",
"version": "0.4.18",
"version": "0.4.19-canary.1",
"license": "MIT",
"peerDependencies": {
"@now/build-utils": ">=0.0.1"
"repository": {
"type": "git",
"url": "https://github.com/zeit/now-builders.git",
"directory": "packages/now-mdx-deck"
},
"scripts": {
"test": "jest"

View File

@@ -7,7 +7,7 @@ const {
testDeployment,
} = require('../../../test/lib/deployment/test-deployment.js');
jest.setTimeout(2 * 60 * 1000);
jest.setTimeout(4 * 60 * 1000);
const buildUtilsUrl = '@canary';
let builderUrl;

View File

@@ -1,23 +1,21 @@
const { createLambda } = require('@now/build-utils/lambda.js');
const download = require('@now/build-utils/fs/download.js');
const FileFsRef = require('@now/build-utils/file-fs-ref.js');
const FileBlob = require('@now/build-utils/file-blob');
const { createLambda } = require('@now/build-utils/lambda.js'); // eslint-disable-line import/no-extraneous-dependencies
const download = require('@now/build-utils/fs/download.js'); // eslint-disable-line import/no-extraneous-dependencies
const FileFsRef = require('@now/build-utils/file-fs-ref.js'); // eslint-disable-line import/no-extraneous-dependencies
const FileBlob = require('@now/build-utils/file-blob'); // eslint-disable-line import/no-extraneous-dependencies
const path = require('path');
const { readFile, writeFile, unlink } = require('fs.promised');
const {
runNpmInstall,
runPackageJsonScript,
} = require('@now/build-utils/fs/run-user-scripts.js');
const glob = require('@now/build-utils/fs/glob.js');
} = require('@now/build-utils/fs/run-user-scripts.js'); // eslint-disable-line import/no-extraneous-dependencies
const glob = require('@now/build-utils/fs/glob.js'); // eslint-disable-line import/no-extraneous-dependencies
const semver = require('semver');
const nextLegacyVersions = require('./legacy-versions');
const {
excludeFiles,
validateEntrypoint,
includeOnlyEntryDirectory,
moveEntryDirectoryToRoot,
normalizePackageJson,
excludeStaticDirectory,
onlyStaticDirectory,
} = require('./utils');
@@ -33,15 +31,17 @@ const {
/**
* Read package.json from files
* @param {DownloadedFiles} files
* @param {string} entryPath
*/
async function readPackageJson(files) {
if (!files['package.json']) {
async function readPackageJson(entryPath) {
const packagePath = path.join(entryPath, 'package.json');
try {
return JSON.parse(await readFile(packagePath, 'utf8'));
} catch (err) {
console.log('package.json not found in entry');
return {};
}
const packageJsonPath = files['package.json'].fsPath;
return JSON.parse(await readFile(packageJsonPath, 'utf8'));
}
/**
@@ -81,20 +81,10 @@ exports.build = async ({ files, workPath, entrypoint }) => {
console.log('downloading user files...');
const entryDirectory = path.dirname(entrypoint);
const filesOnlyEntryDirectory = includeOnlyEntryDirectory(
files,
entryDirectory,
);
const filesWithEntryDirectoryRoot = moveEntryDirectoryToRoot(
filesOnlyEntryDirectory,
entryDirectory,
);
const filesWithoutStaticDirectory = excludeStaticDirectory(
filesWithEntryDirectoryRoot,
);
const downloadedFiles = await download(filesWithoutStaticDirectory, workPath);
await download(files, workPath);
const entryPath = path.join(workPath, entryDirectory);
const pkg = await readPackageJson(downloadedFiles);
const pkg = await readPackageJson(entryPath);
let nextVersion;
if (pkg.dependencies && pkg.dependencies.next) {
@@ -133,24 +123,25 @@ exports.build = async ({ files, workPath, entrypoint }) => {
if (isLegacy) {
try {
await unlink(path.join(workPath, 'yarn.lock'));
await unlink(path.join(entryPath, 'yarn.lock'));
} catch (err) {
console.log('no yarn.lock removed');
}
try {
await unlink(path.join(workPath, 'package-lock.json'));
await unlink(path.join(entryPath, 'package-lock.json'));
} catch (err) {
console.log('no package-lock.json removed');
}
console.warn(
"WARNING: your application is being deployed in @now/next's legacy mode.",
"WARNING: your application is being deployed in @now/next's legacy mode. http://err.sh/zeit/now-builders/now-next-legacy-mode",
);
console.log('normalizing package.json');
const packageJson = normalizePackageJson(pkg);
console.log('normalized package.json result: ', packageJson);
await writePackageJson(workPath, packageJson);
await writePackageJson(entryPath, packageJson);
} else if (!pkg.scripts || !pkg.scripts['now-build']) {
console.warn(
'WARNING: "now-build" script not found. Adding \'"now-build": "next build"\' to "package.json" automatically',
@@ -160,38 +151,38 @@ exports.build = async ({ files, workPath, entrypoint }) => {
...(pkg.scripts || {}),
};
console.log('normalized package.json result: ', pkg);
await writePackageJson(workPath, pkg);
await writePackageJson(entryPath, pkg);
}
if (process.env.NPM_AUTH_TOKEN) {
console.log('found NPM_AUTH_TOKEN in environment, creating .npmrc');
await writeNpmRc(workPath, process.env.NPM_AUTH_TOKEN);
await writeNpmRc(entryPath, process.env.NPM_AUTH_TOKEN);
}
console.log('installing dependencies...');
await runNpmInstall(workPath, ['--prefer-offline']);
await runNpmInstall(entryPath, ['--prefer-offline']);
console.log('running user script...');
await runPackageJsonScript(workPath, 'now-build');
await runPackageJsonScript(entryPath, 'now-build');
if (isLegacy) {
console.log('running npm install --production...');
await runNpmInstall(workPath, ['--prefer-offline', '--production']);
await runNpmInstall(entryPath, ['--prefer-offline', '--production']);
}
if (process.env.NPM_AUTH_TOKEN) {
await unlink(path.join(workPath, '.npmrc'));
await unlink(path.join(entryPath, '.npmrc'));
}
const lambdas = {};
if (isLegacy) {
const filesAfterBuild = await glob('**', workPath);
const filesAfterBuild = await glob('**', entryPath);
console.log('preparing lambda files...');
let buildId;
try {
buildId = await readFile(
path.join(workPath, '.next', 'BUILD_ID'),
path.join(entryPath, '.next', 'BUILD_ID'),
'utf8',
);
} catch (err) {
@@ -200,10 +191,10 @@ exports.build = async ({ files, workPath, entrypoint }) => {
);
throw new Error('Missing BUILD_ID');
}
const dotNextRootFiles = await glob('.next/*', workPath);
const dotNextServerRootFiles = await glob('.next/server/*', workPath);
const dotNextRootFiles = await glob('.next/*', entryPath);
const dotNextServerRootFiles = await glob('.next/server/*', entryPath);
const nodeModules = excludeFiles(
await glob('node_modules/**', workPath),
await glob('node_modules/**', entryPath),
file => file.startsWith('node_modules/.cache'),
);
const launcherFiles = {
@@ -220,7 +211,7 @@ exports.build = async ({ files, workPath, entrypoint }) => {
}
const pages = await glob(
'**/*.js',
path.join(workPath, '.next', 'server', 'static', buildId, 'pages'),
path.join(entryPath, '.next', 'server', 'static', buildId, 'pages'),
);
const launcherPath = path.join(__dirname, 'legacy-launcher.js');
const launcherData = await readFile(launcherPath, 'utf8');
@@ -276,7 +267,7 @@ exports.build = async ({ files, workPath, entrypoint }) => {
};
const pages = await glob(
'**/*.js',
path.join(workPath, '.next', 'serverless', 'pages'),
path.join(entryPath, '.next', 'serverless', 'pages'),
);
const pageKeys = Object.keys(pages);
@@ -312,7 +303,7 @@ exports.build = async ({ files, workPath, entrypoint }) => {
const nextStaticFiles = await glob(
'**',
path.join(workPath, '.next', 'static'),
path.join(entryPath, '.next', 'static'),
);
const staticFiles = Object.keys(nextStaticFiles).reduce(
(mappedFiles, file) => ({
@@ -322,7 +313,9 @@ exports.build = async ({ files, workPath, entrypoint }) => {
{},
);
const nextStaticDirectory = onlyStaticDirectory(filesWithEntryDirectoryRoot);
const nextStaticDirectory = onlyStaticDirectory(
includeOnlyEntryDirectory(files, entryDirectory),
);
const staticDirectoryFiles = Object.keys(nextStaticDirectory).reduce(
(mappedFiles, file) => ({
...mappedFiles,

View File

@@ -1,14 +1,16 @@
{
"name": "@now/next",
"version": "0.0.84",
"version": "0.0.85-canary.5",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/zeit/now-builders.git",
"directory": "packages/now-next"
},
"dependencies": {
"@now/node-bridge": "0.1.4",
"execa": "^1.0.0",
"fs.promised": "^3.0.0",
"semver": "^5.6.0"
},
"peerDependencies": {
"@now/build-utils": ">=0.0.1"
}
}

View File

@@ -1,5 +1,3 @@
const rename = require('@now/build-utils/fs/rename.js');
/** @typedef { import('@now/build-utils/file-ref') } FileRef */
/** @typedef { import('@now/build-utils/file-fs-ref') } FileFsRef */
/** @typedef {{[filePath: string]: FileRef|FileFsRef}} Files */
@@ -64,24 +62,6 @@ function includeOnlyEntryDirectory(files, entryDirectory) {
return excludeFiles(files, matcher);
}
/**
* Moves all files under the entry directory to the root directory
* @param {Files} files
* @param {string} entryDirectory
* @returns {Files}
*/
function moveEntryDirectoryToRoot(files, entryDirectory) {
if (entryDirectory === '.') {
return files;
}
function delegate(filePath) {
return filePath.replace(new RegExp(`^${entryDirectory}/`), '');
}
return rename(files, delegate);
}
/**
* Exclude package manager lockfiles from files
* @param {Files} files
@@ -98,19 +78,6 @@ function excludeLockFiles(files) {
return files;
}
/**
* Exclude the static directory from files
* @param {Files} files
* @returns {Files}
*/
function excludeStaticDirectory(files) {
function matcher(filePath) {
return filePath.startsWith('static');
}
return excludeFiles(files, matcher);
}
/**
* Exclude the static directory from files
* @param {Files} files
@@ -173,9 +140,7 @@ module.exports = {
excludeFiles,
validateEntrypoint,
includeOnlyEntryDirectory,
moveEntryDirectoryToRoot,
excludeLockFiles,
normalizePackageJson,
excludeStaticDirectory,
onlyStaticDirectory,
};

View File

@@ -0,0 +1,24 @@
{
"extends": ["prettier", "airbnb-base"],
"rules": {
"no-console": 0,
"import/no-unresolved": 0,
"import/no-dynamic-require": 0,
"global-require": 0
},
"overrides": [
{
"files": ["test/**"],
"rules": {
"import/no-extraneous-dependencies": 0
},
"globals": {
"describe": true,
"it": true,
"test": true,
"expect": true
}
}
]
}

1
packages/now-node-bridge/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/bridge.*

View File

@@ -1,110 +0,0 @@
const http = require('http');
function normalizeEvent(event) {
let isApiGateway = true;
if (event.Action === 'Invoke') {
isApiGateway = false;
const invokeEvent = JSON.parse(event.body);
const {
method, path, headers, encoding,
} = invokeEvent;
let { body } = invokeEvent;
if (body) {
if (encoding === 'base64') {
body = Buffer.from(body, encoding);
} else if (encoding === undefined) {
body = Buffer.from(body);
} else {
throw new Error(`Unsupported encoding: ${encoding}`);
}
}
return {
isApiGateway, method, path, headers, body,
};
}
const {
httpMethod: method, path, headers, body,
} = event;
return {
isApiGateway, method, path, headers, body,
};
}
class Bridge {
constructor() {
this.launcher = this.launcher.bind(this);
}
launcher(event) {
// eslint-disable-next-line consistent-return
return new Promise((resolve, reject) => {
if (this.userError) {
console.error('Error while initializing entrypoint:', this.userError);
return resolve({ statusCode: 500, body: '' });
}
if (!this.port) {
return resolve({ statusCode: 504, body: '' });
}
const {
isApiGateway, method, path, headers, body,
} = normalizeEvent(event);
const opts = {
hostname: '127.0.0.1',
port: this.port,
path,
method,
headers,
};
const req = http.request(opts, (res) => {
const response = res;
const respBodyChunks = [];
response.on('data', chunk => respBodyChunks.push(Buffer.from(chunk)));
response.on('error', reject);
response.on('end', () => {
const bodyBuffer = Buffer.concat(respBodyChunks);
delete response.headers.connection;
if (isApiGateway) {
delete response.headers['content-length'];
} else
if (response.headers['content-length']) {
response.headers['content-length'] = bodyBuffer.length;
}
resolve({
statusCode: response.statusCode,
headers: response.headers,
body: bodyBuffer.toString('base64'),
encoding: 'base64',
});
});
});
req.on('error', (error) => {
setTimeout(() => {
// this lets express print the true error of why the connection was closed.
// it is probably 'Cannot set headers after they are sent to the client'
reject(error);
}, 2);
});
if (body) req.write(body);
req.end();
});
}
}
module.exports = {
Bridge,
};

View File

@@ -1,8 +1,26 @@
{
"name": "@now/node-bridge",
"version": "0.1.10",
"version": "1.0.0-canary.2",
"license": "MIT",
"peerDependencies": {
"@now/build-utils": ">=0.0.1"
"main": "./index.js",
"repository": {
"type": "git",
"url": "https://github.com/zeit/now-builders.git",
"directory": "packages/now-node-bridge"
},
"files": [
"bridge.*",
"index.js"
],
"scripts": {
"build": "tsc",
"test": "npm run build && jest",
"prepublish": "npm run build"
},
"devDependencies": {
"@types/aws-lambda": "8.10.19",
"@types/node": "11.9.4",
"jest": "24.1.0",
"typescript": "3.3.3"
}
}

View File

@@ -0,0 +1,183 @@
import { AddressInfo } from 'net';
import { APIGatewayProxyEvent } from 'aws-lambda';
import {
Server,
IncomingHttpHeaders,
OutgoingHttpHeaders,
request
} from 'http';
interface NowProxyEvent {
Action: string;
body: string;
}
export interface NowProxyRequest {
isApiGateway?: boolean;
method: string;
path: string;
headers: IncomingHttpHeaders;
body: Buffer;
}
export interface NowProxyResponse {
statusCode: number;
headers: OutgoingHttpHeaders;
body: string;
encoding: string;
}
function normalizeNowProxyEvent(event: NowProxyEvent): NowProxyRequest {
let bodyBuffer: Buffer | null;
const { method, path, headers, encoding, body } = JSON.parse(event.body);
if (body) {
if (encoding === 'base64') {
bodyBuffer = Buffer.from(body, encoding);
} else if (encoding === undefined) {
bodyBuffer = Buffer.from(body);
} else {
throw new Error(`Unsupported encoding: ${encoding}`);
}
} else {
bodyBuffer = Buffer.alloc(0);
}
return { isApiGateway: false, method, path, headers, body: bodyBuffer };
}
function normalizeAPIGatewayProxyEvent(
event: APIGatewayProxyEvent
): NowProxyRequest {
let bodyBuffer: Buffer | null;
const { httpMethod: method, path, headers, body } = event;
if (body) {
if (event.isBase64Encoded) {
bodyBuffer = Buffer.from(body, 'base64');
} else {
bodyBuffer = Buffer.from(body);
}
} else {
bodyBuffer = Buffer.alloc(0);
}
return { isApiGateway: true, method, path, headers, body: bodyBuffer };
}
function normalizeEvent(
event: NowProxyEvent | APIGatewayProxyEvent
): NowProxyRequest {
if ('Action' in event) {
if (event.Action === 'Invoke') {
return normalizeNowProxyEvent(event);
} else {
throw new Error(`Unexpected event.Action: ${event.Action}`);
}
} else {
return normalizeAPIGatewayProxyEvent(event);
}
}
export class Bridge {
private server: Server | null;
private listening: Promise<AddressInfo>;
private resolveListening: (info: AddressInfo) => void;
constructor(server?: Server) {
this.server = null;
if (server) {
this.setServer(server);
}
this.launcher = this.launcher.bind(this);
// This is just to appease TypeScript strict mode, since it doesn't
// understand that the Promise constructor is synchronous
this.resolveListening = (info: AddressInfo) => {};
this.listening = new Promise(resolve => {
this.resolveListening = resolve;
});
}
setServer(server: Server) {
this.server = server;
server.once('listening', () => {
const addr = server.address();
if (typeof addr === 'string') {
throw new Error(`Unexpected string for \`server.address()\`: ${addr}`);
} else if (!addr) {
throw new Error('`server.address()` returned `null`');
} else {
this.resolveListening(addr);
}
});
}
listen() {
if (!this.server) {
throw new Error('Server has not been set!');
}
return this.server.listen({
host: '127.0.0.1',
port: 0
});
}
async launcher(
event: NowProxyEvent | APIGatewayProxyEvent
): Promise<NowProxyResponse> {
const { port } = await this.listening;
const { isApiGateway, method, path, headers, body } = normalizeEvent(
event
);
const opts = {
hostname: '127.0.0.1',
port,
path,
method,
headers
};
// eslint-disable-next-line consistent-return
return new Promise((resolve, reject) => {
const req = request(opts, res => {
const response = res;
const respBodyChunks: Buffer[] = [];
response.on('data', chunk => respBodyChunks.push(Buffer.from(chunk)));
response.on('error', reject);
response.on('end', () => {
const bodyBuffer = Buffer.concat(respBodyChunks);
delete response.headers.connection;
if (isApiGateway) {
delete response.headers['content-length'];
} else if (response.headers['content-length']) {
response.headers['content-length'] = String(bodyBuffer.length);
}
resolve({
statusCode: response.statusCode || 200,
headers: response.headers,
body: bodyBuffer.toString('base64'),
encoding: 'base64'
});
});
});
req.on('error', error => {
setTimeout(() => {
// this lets express print the true error of why the connection was closed.
// it is probably 'Cannot set headers after they are sent to the client'
reject(error);
}, 2);
});
if (body) req.write(body);
req.end();
});
}
}

View File

@@ -0,0 +1,71 @@
const assert = require('assert');
const { Server } = require('http');
const { Bridge } = require('../bridge');
test('port binding', async () => {
const server = new Server();
const bridge = new Bridge(server);
bridge.listen();
// Test port binding
const info = await bridge.listening;
assert.equal(info.address, '127.0.0.1');
assert.equal(typeof info.port, 'number');
server.close();
});
test('`APIGatewayProxyEvent` normalizing', async () => {
const server = new Server((req, res) => res.end(
JSON.stringify({
method: req.method,
path: req.url,
headers: req.headers,
}),
));
const bridge = new Bridge(server);
bridge.listen();
const result = await bridge.launcher({
httpMethod: 'GET',
headers: { foo: 'bar' },
path: '/apigateway',
body: null,
});
assert.equal(result.encoding, 'base64');
assert.equal(result.statusCode, 200);
const body = JSON.parse(Buffer.from(result.body, 'base64').toString());
assert.equal(body.method, 'GET');
assert.equal(body.path, '/apigateway');
assert.equal(body.headers.foo, 'bar');
server.close();
});
test('`NowProxyEvent` normalizing', async () => {
const server = new Server((req, res) => res.end(
JSON.stringify({
method: req.method,
path: req.url,
headers: req.headers,
}),
));
const bridge = new Bridge(server);
bridge.listen();
const result = await bridge.launcher({
Action: 'Invoke',
body: JSON.stringify({
method: 'POST',
headers: { foo: 'baz' },
path: '/nowproxy',
body: 'body=1',
}),
});
assert.equal(result.encoding, 'base64');
assert.equal(result.statusCode, 200);
const body = JSON.parse(Buffer.from(result.body, 'base64').toString());
assert.equal(body.method, 'POST');
assert.equal(body.path, '/nowproxy');
assert.equal(body.headers.foo, 'baz');
server.close();
});

View File

@@ -0,0 +1,16 @@
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"outDir": ".",
"strict": true,
"sourceMap": true,
"declaration": true
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules"
]
}

View File

@@ -1,15 +1,15 @@
const { createLambda } = require('@now/build-utils/lambda.js');
const download = require('@now/build-utils/fs/download.js');
const FileBlob = require('@now/build-utils/file-blob.js');
const FileFsRef = require('@now/build-utils/file-fs-ref.js');
const { createLambda } = require('@now/build-utils/lambda.js'); // eslint-disable-line import/no-extraneous-dependencies
const download = require('@now/build-utils/fs/download.js'); // eslint-disable-line import/no-extraneous-dependencies
const FileBlob = require('@now/build-utils/file-blob.js'); // eslint-disable-line import/no-extraneous-dependencies
const FileFsRef = require('@now/build-utils/file-fs-ref.js'); // eslint-disable-line import/no-extraneous-dependencies
const fs = require('fs-extra');
const glob = require('@now/build-utils/fs/glob.js');
const glob = require('@now/build-utils/fs/glob.js'); // eslint-disable-line import/no-extraneous-dependencies
const path = require('path');
const rename = require('@now/build-utils/fs/rename.js');
const rename = require('@now/build-utils/fs/rename.js'); // eslint-disable-line import/no-extraneous-dependencies
const {
runNpmInstall,
runPackageJsonScript,
} = require('@now/build-utils/fs/run-user-scripts.js');
} = require('@now/build-utils/fs/run-user-scripts.js'); // eslint-disable-line import/no-extraneous-dependencies
/** @typedef { import('@now/build-utils/file-ref') } FileRef */
/** @typedef {{[filePath: string]: FileRef}} Files */
@@ -37,7 +37,7 @@ async function downloadInstallAndBundle(
console.log('downloading user files...');
const downloadedFiles = await download(files, userPath);
console.log('installing dependencies for user\'s code...');
console.log("installing dependencies for user's code...");
const entrypointFsDirname = path.join(userPath, path.dirname(entrypoint));
await runNpmInstall(entrypointFsDirname, npmArguments);
@@ -46,8 +46,9 @@ async function downloadInstallAndBundle(
{
'package.json': new FileBlob({
data: JSON.stringify({
license: 'UNLICENSED',
dependencies: {
'@zeit/ncc': '0.11.0',
'@zeit/ncc': '0.15.2',
},
}),
}),
@@ -63,7 +64,7 @@ async function downloadInstallAndBundle(
async function compile(workNccPath, downloadedFiles, entrypoint) {
const input = downloadedFiles[entrypoint].fsPath;
const ncc = require(path.join(workNccPath, 'node_modules/@zeit/ncc'));
const { code, assets } = await ncc(input);
const { code, assets } = await ncc(input, { sourceMap: true });
const preparedFiles = {};
const blob = new FileBlob({ data: code });

View File

@@ -4,22 +4,16 @@ const { Bridge } = require('./bridge.js');
const bridge = new Bridge();
const saveListen = Server.prototype.listen;
Server.prototype.listen = function listen(...args) {
this.on('listening', function listening() {
bridge.port = this.address().port;
});
saveListen.apply(this, args);
Server.prototype.listen = function listen() {
bridge.setServer(this);
Server.prototype.listen = saveListen;
return bridge.listen();
};
try {
if (!process.env.NODE_ENV) {
process.env.NODE_ENV = 'production';
}
// PLACEHOLDER
} catch (error) {
console.error(error);
bridge.userError = error;
if (!process.env.NODE_ENV) {
process.env.NODE_ENV = 'production';
}
// PLACEHOLDER
exports.launcher = bridge.launcher;

View File

@@ -1,13 +1,15 @@
{
"name": "@now/node-server",
"version": "0.4.26",
"version": "0.5.0-canary.1",
"license": "MIT",
"dependencies": {
"@now/node-bridge": "^0.1.10",
"fs-extra": "7.0.1"
"repository": {
"type": "git",
"url": "https://github.com/zeit/now-builders.git",
"directory": "packages/now-node-server"
},
"peerDependencies": {
"@now/build-utils": ">=0.0.1"
"dependencies": {
"@now/node-bridge": "1.0.0-canary.2",
"fs-extra": "7.0.1"
},
"scripts": {
"test": "jest"

View File

@@ -7,7 +7,7 @@ const {
testDeployment,
} = require('../../../test/lib/deployment/test-deployment.js');
jest.setTimeout(2 * 60 * 1000);
jest.setTimeout(4 * 60 * 1000);
const buildUtilsUrl = '@canary';
let builderUrl;

1
packages/now-node/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/dist

View File

@@ -1 +0,0 @@
/test

View File

@@ -1,22 +0,0 @@
const { Server } = require('http');
const { Bridge } = require('./bridge.js');
const bridge = new Bridge();
bridge.port = 3000;
let listener;
try {
if (!process.env.NODE_ENV) {
process.env.NODE_ENV = 'production';
}
// PLACEHOLDER
} catch (error) {
console.error(error);
bridge.userError = error;
}
const server = new Server(listener);
server.listen(bridge.port);
exports.launcher = bridge.launcher;

View File

@@ -1,15 +1,28 @@
{
"name": "@now/node",
"version": "0.4.28",
"version": "0.5.0-canary.1",
"license": "MIT",
"main": "./dist/index",
"repository": {
"type": "git",
"url": "https://github.com/zeit/now-builders.git",
"directory": "packages/now-node"
},
"dependencies": {
"@now/node-bridge": "^0.1.10",
"@now/node-bridge": "1.0.0-canary.2",
"fs-extra": "7.0.1"
},
"peerDependencies": {
"@now/build-utils": ">=0.0.1"
},
"scripts": {
"build": "tsc",
"prepublish": "npm run build",
"test": "jest"
},
"files": [
"dist"
],
"devDependencies": {
"@types/node": "11.9.4",
"jest": "24.1.0",
"typescript": "3.3.3"
}
}

30
packages/now-node/src/bridge.d.ts vendored Normal file
View File

@@ -0,0 +1,30 @@
/// <reference types="node" />
import { APIGatewayProxyEvent } from 'aws-lambda';
import { Server, IncomingHttpHeaders, OutgoingHttpHeaders } from 'http';
interface NowProxyEvent {
Action: string;
body: string;
}
export interface NowProxyRequest {
isApiGateway?: boolean;
method: string;
path: string;
headers: IncomingHttpHeaders;
body: Buffer;
}
export interface NowProxyResponse {
statusCode: number;
headers: OutgoingHttpHeaders;
body: string;
encoding: string;
}
export declare class Bridge {
private server;
private listening;
private resolveListening;
constructor(server?: Server);
setServer(server: Server): void;
listen(): Server;
launcher(event: NowProxyEvent | APIGatewayProxyEvent): Promise<NowProxyResponse>;
}
export {};

View File

@@ -1,14 +1,14 @@
const { createLambda } = require('@now/build-utils/lambda.js');
const download = require('@now/build-utils/fs/download.js');
const FileBlob = require('@now/build-utils/file-blob.js');
const FileFsRef = require('@now/build-utils/file-fs-ref.js');
const fs = require('fs-extra');
const glob = require('@now/build-utils/fs/glob.js');
const path = require('path');
const {
import { join, dirname } from 'path';
import { remove, readFile } from 'fs-extra';
import * as glob from '@now/build-utils/fs/glob.js';
import * as download from '@now/build-utils/fs/download.js';
import * as FileBlob from '@now/build-utils/file-blob.js';
import * as FileFsRef from '@now/build-utils/file-fs-ref.js';
import { createLambda } from '@now/build-utils/lambda.js';
import {
runNpmInstall,
runPackageJsonScript,
} = require('@now/build-utils/fs/run-user-scripts.js');
runPackageJsonScript
} from '@now/build-utils/fs/run-user-scripts.js';
/** @typedef { import('@now/build-utils/file-ref') } FileRef */
/** @typedef {{[filePath: string]: FileRef}} Files */
@@ -27,16 +27,16 @@ const {
*/
async function downloadInstallAndBundle(
{ files, entrypoint, workPath },
{ npmArguments = [] } = {},
{ npmArguments = [] } = {}
) {
const userPath = path.join(workPath, 'user');
const nccPath = path.join(workPath, 'ncc');
const userPath = join(workPath, 'user');
const nccPath = join(workPath, 'ncc');
console.log('downloading user files...');
const downloadedFiles = await download(files, userPath);
console.log('installing dependencies for user\'s code...');
const entrypointFsDirname = path.join(userPath, path.dirname(entrypoint));
console.log("installing dependencies for user's code...");
const entrypointFsDirname = join(userPath, dirname(entrypoint));
await runNpmInstall(entrypointFsDirname, npmArguments);
console.log('writing ncc package.json...');
@@ -44,13 +44,14 @@ async function downloadInstallAndBundle(
{
'package.json': new FileBlob({
data: JSON.stringify({
license: 'UNLICENSED',
dependencies: {
'@zeit/ncc': '0.11.0',
},
}),
}),
'@zeit/ncc': '0.15.2',
}
})
})
},
nccPath,
nccPath
);
console.log('installing dependencies for ncc...');
@@ -58,43 +59,41 @@ async function downloadInstallAndBundle(
return [downloadedFiles, nccPath, entrypointFsDirname];
}
async function compile(workNccPath, downloadedFiles, entrypoint) {
async function compile(workNccPath: string, downloadedFiles, entrypoint: string) {
const input = downloadedFiles[entrypoint].fsPath;
const ncc = require(path.join(workNccPath, 'node_modules/@zeit/ncc'));
const ncc = require(join(workNccPath, 'node_modules/@zeit/ncc'));
const { code, assets } = await ncc(input);
const preparedFiles = {};
const blob = new FileBlob({ data: code });
// move all user code to 'user' subdirectory
preparedFiles[path.join('user', entrypoint)] = blob;
preparedFiles[join('user', entrypoint)] = blob;
// eslint-disable-next-line no-restricted-syntax
for (const assetName of Object.keys(assets)) {
const { source: data, permissions: mode } = assets[assetName];
const blob2 = new FileBlob({ data, mode });
preparedFiles[
path.join('user', path.dirname(entrypoint), assetName)
] = blob2;
preparedFiles[join('user', dirname(entrypoint), assetName)] = blob2;
}
return preparedFiles;
}
exports.config = {
maxLambdaSize: '5mb',
export const config = {
maxLambdaSize: '5mb'
};
/**
* @param {BuildParamsType} buildParams
* @returns {Promise<Files>}
*/
exports.build = async ({ files, entrypoint, workPath }) => {
export async function build({ files, entrypoint, workPath }) {
const [
downloadedFiles,
workNccPath,
entrypointFsDirname,
entrypointFsDirname
] = await downloadInstallAndBundle(
{ files, entrypoint, workPath },
{ npmArguments: ['--prefer-offline'] },
{ npmArguments: ['--prefer-offline'] }
);
console.log('running user script...');
@@ -102,36 +101,34 @@ exports.build = async ({ files, entrypoint, workPath }) => {
console.log('compiling entrypoint with ncc...');
const preparedFiles = await compile(workNccPath, downloadedFiles, entrypoint);
const launcherPath = path.join(__dirname, 'launcher.js');
let launcherData = await fs.readFile(launcherPath, 'utf8');
const launcherPath = join(__dirname, 'launcher.js');
let launcherData = await readFile(launcherPath, 'utf8');
launcherData = launcherData.replace(
'// PLACEHOLDER',
[
'process.chdir("./user");',
`listener = require("./${path.join('user', entrypoint)}");`,
'if (listener.default) listener = listener.default;',
].join(' '),
`listener = require("./${join('user', entrypoint)}");`,
'if (listener.default) listener = listener.default;'
].join(' ')
);
const launcherFiles = {
'launcher.js': new FileBlob({ data: launcherData }),
'bridge.js': new FileFsRef({ fsPath: require('@now/node-bridge') }),
'bridge.js': new FileFsRef({ fsPath: require('@now/node-bridge') })
};
const lambda = await createLambda({
files: { ...preparedFiles, ...launcherFiles },
handler: 'launcher.launcher',
runtime: 'nodejs8.10',
runtime: 'nodejs8.10'
});
return { [entrypoint]: lambda };
};
}
exports.prepareCache = async ({
files, entrypoint, workPath, cachePath,
}) => {
await fs.remove(workPath);
export async function prepareCache({ files, entrypoint, workPath, cachePath }) {
await remove(workPath);
await downloadInstallAndBundle({ files, entrypoint, workPath: cachePath });
return {
@@ -140,6 +137,6 @@ exports.prepareCache = async ({
...(await glob('user/yarn.lock', cachePath)),
...(await glob('ncc/node_modules/**', cachePath)),
...(await glob('ncc/package-lock.json', cachePath)),
...(await glob('ncc/yarn.lock', cachePath)),
...(await glob('ncc/yarn.lock', cachePath))
};
};
}

View File

@@ -0,0 +1,16 @@
import { Server } from 'http';
import { Bridge } from './bridge';
let listener;
if (!process.env.NODE_ENV) {
process.env.NODE_ENV = 'production';
}
// PLACEHOLDER
const server = new Server(listener);
const bridge = new Bridge(server);
bridge.listen();
exports.launcher = bridge.launcher;

View File

@@ -7,7 +7,7 @@ const {
testDeployment,
} = require('../../../test/lib/deployment/test-deployment.js');
jest.setTimeout(2 * 60 * 1000);
jest.setTimeout(4 * 60 * 1000);
const buildUtilsUrl = '@canary';
let builderUrl;

View File

@@ -0,0 +1,15 @@
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"outDir": "dist",
"sourceMap": false,
"declaration": false
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules"
]
}

View File

@@ -1,4 +1,4 @@
const FileBlob = require('@now/build-utils/file-blob.js');
const FileBlob = require('@now/build-utils/file-blob.js'); // eslint-disable-line import/no-extraneous-dependencies
const OptiPng = require('optipng');
const pipe = require('multipipe');

View File

@@ -1,12 +1,14 @@
{
"name": "@now/optipng",
"version": "0.4.8",
"version": "0.4.9-canary.0",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/zeit/now-builders.git",
"directory": "packages/now-optipng"
},
"dependencies": {
"multipipe": "2.0.3",
"optipng": "1.1.0"
},
"peerDependencies": {
"@now/build-utils": ">=0.0.1"
}
}

View File

@@ -1,23 +1,38 @@
const FileBlob = require('@now/build-utils/file-blob.js');
const FileFsRef = require('@now/build-utils/file-fs-ref.js');
const glob = require('@now/build-utils/fs/glob.js');
const FileBlob = require('@now/build-utils/file-blob.js'); // eslint-disable-line import/no-extraneous-dependencies
const FileFsRef = require('@now/build-utils/file-fs-ref.js'); // eslint-disable-line import/no-extraneous-dependencies
const glob = require('@now/build-utils/fs/glob.js'); // eslint-disable-line import/no-extraneous-dependencies
const path = require('path');
async function getFiles() {
const files = await glob('native/**', __dirname);
const phpConfig = await FileBlob.fromStream({ stream: files['native/php.ini'].toStream() });
phpConfig.data = phpConfig.data.toString()
const phpConfig = await FileBlob.fromStream({
stream: files['native/php.ini'].toStream(),
});
phpConfig.data = phpConfig.data
.toString()
.replace(/\/root\/app\/modules/g, '/var/task/native/modules');
files['native/php.ini'] = phpConfig;
Object.assign(files, {
'fastcgi/connection.js': new FileFsRef({ fsPath: require.resolve('fastcgi-client/lib/connection.js') }),
'fastcgi/consts.js': new FileFsRef({ fsPath: require.resolve('fastcgi-client/lib/consts.js') }),
'fastcgi/stringifykv.js': new FileFsRef({ fsPath: require.resolve('fastcgi-client/lib/stringifykv.js') }),
'fastcgi/index.js': new FileFsRef({ fsPath: path.join(__dirname, 'fastcgi/index.js') }),
'fastcgi/port.js': new FileFsRef({ fsPath: path.join(__dirname, 'fastcgi/port.js') }),
'launcher.js': new FileFsRef({ fsPath: path.join(__dirname, 'launcher.js') }),
'fastcgi/connection.js': new FileFsRef({
fsPath: require.resolve('fastcgi-client/lib/connection.js'),
}),
'fastcgi/consts.js': new FileFsRef({
fsPath: require.resolve('fastcgi-client/lib/consts.js'),
}),
'fastcgi/stringifykv.js': new FileFsRef({
fsPath: require.resolve('fastcgi-client/lib/stringifykv.js'),
}),
'fastcgi/index.js': new FileFsRef({
fsPath: path.join(__dirname, 'fastcgi/index.js'),
}),
'fastcgi/port.js': new FileFsRef({
fsPath: path.join(__dirname, 'fastcgi/port.js'),
}),
'launcher.js': new FileFsRef({
fsPath: path.join(__dirname, 'launcher.js'),
}),
});
return files;

View File

@@ -1,11 +1,13 @@
{
"name": "@now/php-bridge",
"version": "0.4.13",
"version": "0.4.14-canary.0",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/zeit/now-builders.git",
"directory": "packages/now-php-bridge"
},
"dependencies": {
"fastcgi-client": "0.0.1"
},
"peerDependencies": {
"@now/build-utils": ">=0.0.1"
}
}

View File

@@ -1,6 +1,6 @@
const { createLambda } = require('@now/build-utils/lambda.js');
const { createLambda } = require('@now/build-utils/lambda.js'); // eslint-disable-line import/no-extraneous-dependencies
const path = require('path');
const rename = require('@now/build-utils/fs/rename.js');
const rename = require('@now/build-utils/fs/rename.js'); // eslint-disable-line import/no-extraneous-dependencies
const { getFiles } = require('@now/php-bridge');
exports.config = {

View File

@@ -1,12 +1,14 @@
{
"name": "@now/php",
"version": "0.4.13",
"version": "0.4.14-canary.1",
"license": "MIT",
"dependencies": {
"@now/php-bridge": "^0.4.13"
"repository": {
"type": "git",
"url": "https://github.com/zeit/now-builders.git",
"directory": "packages/now-php"
},
"peerDependencies": {
"@now/build-utils": ">=0.0.1"
"dependencies": {
"@now/php-bridge": "^0.4.14-canary.0"
},
"scripts": {
"test": "jest"

View File

@@ -7,7 +7,7 @@ const {
testDeployment,
} = require('../../../test/lib/deployment/test-deployment.js');
jest.setTimeout(2 * 60 * 1000);
jest.setTimeout(4 * 60 * 1000);
const buildUtilsUrl = '@canary';
let builderUrl;

View File

@@ -3,7 +3,7 @@ const fetch = require('node-fetch');
const execa = require('execa');
const { createWriteStream } = require('fs');
const getWritableDirectory = require('@now/build-utils/fs/get-writable-directory.js');
const getWritableDirectory = require('@now/build-utils/fs/get-writable-directory.js'); // eslint-disable-line import/no-extraneous-dependencies
const url = 'https://bootstrap.pypa.io/get-pip.py';

View File

@@ -1,10 +1,10 @@
const path = require('path');
const execa = require('execa');
const { readFile, writeFile } = require('fs.promised');
const getWritableDirectory = require('@now/build-utils/fs/get-writable-directory.js');
const download = require('@now/build-utils/fs/download.js');
const glob = require('@now/build-utils/fs/glob.js');
const { createLambda } = require('@now/build-utils/lambda.js');
const getWritableDirectory = require('@now/build-utils/fs/get-writable-directory.js'); // eslint-disable-line import/no-extraneous-dependencies
const download = require('@now/build-utils/fs/download.js'); // eslint-disable-line import/no-extraneous-dependencies
const glob = require('@now/build-utils/fs/glob.js'); // eslint-disable-line import/no-extraneous-dependencies
const { createLambda } = require('@now/build-utils/lambda.js'); // eslint-disable-line import/no-extraneous-dependencies
const downloadAndInstallPip = require('./download-and-install-pip');
async function pipInstall(pipPath, srcDir, ...args) {
@@ -40,8 +40,16 @@ exports.build = async ({ files, entrypoint }) => {
await pipInstall(pipPath, srcDir, 'requests');
if (files['requirements.txt']) {
console.log('found "requirements.txt"');
const entryDirectory = path.dirname(entrypoint);
const requirementsTxt = path.join(entryDirectory, 'requirements.txt');
if (files[requirementsTxt]) {
console.log('found local "requirements.txt"');
const requirementsTxtPath = files[requirementsTxt].fsPath;
await pipInstall(pipPath, srcDir, '-r', requirementsTxtPath);
} else if (files['requirements.txt']) {
console.log('found global "requirements.txt"');
const requirementsTxtPath = files['requirements.txt'].fsPath;
await pipInstall(pipPath, srcDir, '-r', requirementsTxtPath);

View File

@@ -1,23 +1,35 @@
import base64
from http.server import HTTPServer
import json
import requests
from __NOW_HANDLER_FILENAME import handler
import _thread
server = HTTPServer(('', 3000), handler)
def now_handler(event, context):
_thread.start_new_thread(server.handle_request, ())
payload = json.loads(event['body'])
path = payload['path']
headers = payload['headers']
method = payload['method']
res = requests.request(method, 'http://0.0.0.0:3000' + path, headers=headers)
encoding = payload.get('encoding')
body = payload.get('body')
if (
(body is not None and len(body) > 0) and
(encoding is not None and encoding == 'base64')
):
body = base64.b64decode(body)
res = requests.request(method, 'http://0.0.0.0:3000' + path,
headers=headers, data=body, allow_redirects=False)
return {
'statusCode': res.status_code,
'headers': dict(res.headers),
'body': res.text
'body': res.text,
}

View File

@@ -1,14 +1,16 @@
{
"name": "@now/python",
"version": "0.0.40",
"version": "0.0.41-canary.2",
"main": "index.js",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/zeit/now-builders.git",
"directory": "packages/now-python"
},
"dependencies": {
"execa": "^1.0.0",
"fs.promised": "^3.0.0",
"node-fetch": "^2.2.0"
},
"peerDependencies": {
"@now/build-utils": ">=0.0.1"
}
}

1101
packages/now-rust/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,25 @@
[package]
name = "now_lambda"
version = "0.1.2"
authors = ["Antonio Nuno Monteiro <anmonteiro@gmail.com>"]
edition = "2018"
description = "Rust bindings for Now.sh Lambdas"
keywords = ["AWS", "Lambda", "Zeit", "Now", "Rust"]
license = "MIT"
homepage = "https://github.com/zeit/now-builders"
repository = "https://github.com/zeit/now-builders"
documentation = "https://docs.rs/now_lambda"
include = [
"src/*.rs",
"Cargo.toml"
]
[dependencies]
serde = "^1"
serde_json = "^1"
serde_derive = "^1"
http = "0.1"
tokio = "^0.1"
base64 = "0.10"
log = "^0.4"
lambda_runtime = "0.2.0"

View File

@@ -0,0 +1,84 @@
const tar = require('tar');
const fetch = require('node-fetch');
const execa = require('execa');
const rustUrl = 'https://dmmcy0pwk6bqi.cloudfront.net/rust.tar.gz';
const ccUrl = 'https://dmmcy0pwk6bqi.cloudfront.net/gcc-4.8.5.tgz';
async function downloadRustToolchain() {
console.log('downloading the rust toolchain');
const res = await fetch(rustUrl);
if (!res.ok) {
throw new Error(`Failed to download: ${rustUrl}`);
}
const { HOME } = process.env;
return new Promise((resolve, reject) => {
res.body
.on('error', reject)
.pipe(tar.extract({ gzip: true, cwd: HOME }))
.on('finish', () => resolve());
});
}
async function downloadGCC() {
console.log('downloading GCC');
const res = await fetch(ccUrl);
if (!res.ok) {
throw new Error(`Failed to download: ${ccUrl}`);
}
return new Promise((resolve, reject) => {
res.body
.on('error', reject)
// NOTE(anmonteiro): We pipe GCC into `/tmp` instead of getting a writable
// directory from `@now/build-utils` because the GCC distribution that we
// use is specifically packaged for AWS Lambda (where `/tmp` is writable)
// and contains several hardcoded symlinks to paths in `/tmp`.
.pipe(tar.extract({ gzip: true, cwd: '/tmp' }))
.on('finish', async () => {
const { LD_LIBRARY_PATH } = process.env;
// Set the environment variables as per
// https://github.com/lambci/lambci/blob/e6c9c7/home/init/gcc#L14-L17
const newEnv = {
PATH: '/tmp/bin:/tmp/sbin',
LD_LIBRARY_PATH: `/tmp/lib:/tmp/lib64:${LD_LIBRARY_PATH}`,
CPATH: '/tmp/include',
LIBRARY_PATH: '/tmp/lib',
};
return resolve(newEnv);
});
});
}
async function installOpenSSL() {
console.log('installing openssl-devel...');
try {
// need to downgrade otherwise yum can't resolve the dependencies given
// a later version is already installed in the machine.
await execa(
'yum',
['downgrade', '-y', 'krb5-libs-1.14.1-27.41.amzn1.x86_64'],
{
stdio: 'inherit',
},
);
await execa('yum', ['install', '-y', 'openssl-devel'], {
stdio: 'inherit',
});
} catch (err) {
console.error('failed to `yum install -y openssl-devel`');
throw err;
}
}
module.exports = async () => {
await downloadRustToolchain();
const newEnv = await downloadGCC();
await installOpenSSL();
return newEnv;
};

270
packages/now-rust/index.js Normal file
View File

@@ -0,0 +1,270 @@
const fs = require('fs-extra');
const path = require('path');
const execa = require('execa');
const toml = require('@iarna/toml');
const { createLambda } = require('@now/build-utils/lambda.js'); // eslint-disable-line import/no-extraneous-dependencies
const download = require('@now/build-utils/fs/download.js'); // eslint-disable-line import/no-extraneous-dependencies
const glob = require('@now/build-utils/fs/glob.js'); // eslint-disable-line import/no-extraneous-dependencies
const FileFsRef = require('@now/build-utils/file-fs-ref.js'); // eslint-disable-line import/no-extraneous-dependencies
const installRustAndGCC = require('./download-install-rust-toolchain.js');
exports.config = {
maxLambdaSize: '25mb',
};
async function inferCargoBinaries(config) {
try {
const { stdout: manifestStr } = await execa(
'cargo',
['read-manifest'],
config,
);
const { targets } = JSON.parse(manifestStr);
return targets
.filter(({ kind }) => kind.includes('bin'))
.map(({ name }) => name);
} catch (err) {
console.error('failed to run `cargo read-manifest`');
throw err;
}
}
async function parseTOMLStream(stream) {
return toml.parse.stream(stream);
}
async function buildWholeProject({
entrypoint,
downloadedFiles,
rustEnv,
config,
}) {
const entrypointDirname = path.dirname(downloadedFiles[entrypoint].fsPath);
const { debug } = config;
console.log('running `cargo build`...');
try {
await execa('cargo', ['build'].concat(debug ? [] : ['--release']), {
env: rustEnv,
cwd: entrypointDirname,
stdio: 'inherit',
});
} catch (err) {
console.error('failed to `cargo build`');
throw err;
}
const targetPath = path.join(
entrypointDirname,
'target',
debug ? 'debug' : 'release',
);
const binaries = await inferCargoBinaries({
env: rustEnv,
cwd: entrypointDirname,
});
const lambdas = {};
const lambdaPath = path.dirname(entrypoint);
await Promise.all(
binaries.map(async (binary) => {
const fsPath = path.join(targetPath, binary);
const lambda = await createLambda({
files: {
bootstrap: new FileFsRef({ mode: 0o755, fsPath }),
},
handler: 'bootstrap',
runtime: 'provided',
});
lambdas[path.join(lambdaPath, binary)] = lambda;
}),
);
return lambdas;
}
async function cargoLocateProject(config) {
try {
const { stdout: projectDescriptionStr } = await execa(
'cargo',
['locate-project'],
config,
);
const projectDescription = JSON.parse(projectDescriptionStr);
if (projectDescription != null && projectDescription.root != null) {
return projectDescription.root;
}
} catch (e) {
if (!/could not find/g.test(e.stderr)) {
console.error("Couldn't run `cargo locate-project`");
throw e;
}
}
return null;
}
async function buildSingleFile({
workPath,
entrypoint,
downloadedFiles,
rustEnv,
config,
}) {
console.log('building single file');
const launcherPath = path.join(__dirname, 'launcher.rs');
let launcherData = await fs.readFile(launcherPath, 'utf8');
const entrypointPath = downloadedFiles[entrypoint].fsPath;
const entrypointDirname = path.dirname(entrypointPath);
launcherData = launcherData.replace(
'// PLACEHOLDER',
await fs.readFile(path.join(workPath, entrypoint)),
);
// replace the entrypoint with one that includes the the imports + lambda.start
await fs.remove(entrypointPath);
await fs.writeFile(entrypointPath, launcherData);
// Find a Cargo.toml file or TODO: create one
const cargoTomlFile = await cargoLocateProject({
env: rustEnv,
cwd: entrypointDirname,
});
// TODO: we're assuming there's a Cargo.toml file. We need to create one
// otherwise
let cargoToml;
try {
cargoToml = await parseTOMLStream(fs.createReadStream(cargoTomlFile));
} catch (err) {
console.error('Failed to parse TOML from entrypoint:', entrypoint);
throw err;
}
const binName = path
.basename(entrypointPath)
.replace(path.extname(entrypointPath), '');
const { package: pkg, dependencies } = cargoToml;
// default to latest now_lambda
dependencies.now_lambda = '*';
const tomlToWrite = toml.stringify({
package: pkg,
dependencies,
bin: [
{
name: binName,
path: entrypointPath,
},
],
});
console.log('toml to write:', tomlToWrite);
// Overwrite the Cargo.toml file with one that includes the `now_lambda`
// dependency and our binary. `dependencies` is a map so we don't run the
// risk of having 2 `now_lambda`s in there.
await fs.writeFile(cargoTomlFile, tomlToWrite);
const { debug } = config;
console.log('running `cargo build`...');
try {
await execa(
'cargo',
['build', '--bin', binName].concat(debug ? [] : ['--release']),
{
env: rustEnv,
cwd: entrypointDirname,
stdio: 'inherit',
},
);
} catch (err) {
console.error('failed to `cargo build`');
throw err;
}
const bin = path.join(
path.dirname(cargoTomlFile),
'target',
debug ? 'debug' : 'release',
binName,
);
const lambda = await createLambda({
files: {
bootstrap: new FileFsRef({ mode: 0o755, fsPath: bin }),
},
handler: 'bootstrap',
runtime: 'provided',
});
return {
[entrypoint]: lambda,
};
}
exports.build = async (m) => {
const { files, entrypoint, workPath } = m;
console.log('downloading files');
const downloadedFiles = await download(files, workPath);
const { PATH: toolchainPath, ...otherEnv } = await installRustAndGCC();
const { PATH, HOME } = process.env;
const rustEnv = {
...process.env,
...otherEnv,
PATH: `${path.join(HOME, '.cargo/bin')}:${toolchainPath}:${PATH}`,
};
const newM = Object.assign(m, { downloadedFiles, rustEnv });
if (path.extname(entrypoint) === '.toml') {
return buildWholeProject(newM);
}
return buildSingleFile(newM);
};
exports.prepareCache = async ({ cachePath, entrypoint, workPath }) => {
console.log('preparing cache...');
let targetFolderDir;
if (path.extname(entrypoint) === '.toml') {
targetFolderDir = path.dirname(path.join(workPath, entrypoint));
} else {
const { PATH, HOME } = process.env;
const rustEnv = {
...process.env,
PATH: `${path.join(HOME, '.cargo/bin')}:${PATH}`,
};
const entrypointDirname = path.dirname(path.join(workPath, entrypoint));
const cargoTomlFile = await cargoLocateProject({
env: rustEnv,
cwd: entrypointDirname,
});
if (cargoTomlFile != null) {
targetFolderDir = path.dirname(cargoTomlFile);
} else {
// `Cargo.toml` doesn't exist, in `build` we put it in the same
// path as the entrypoint.
targetFolderDir = path.dirname(path.join(workPath, entrypoint));
}
}
const cacheEntrypointDirname = path.join(
cachePath,
path.relative(workPath, targetFolderDir),
);
// Remove the target folder to avoid 'directory already exists' errors
fs.removeSync(path.join(cacheEntrypointDirname, 'target'));
fs.mkdirpSync(cacheEntrypointDirname);
// Move the target folder to the cache location
fs.renameSync(
path.join(targetFolderDir, 'target'),
path.join(cacheEntrypointDirname, 'target'),
);
return {
...(await glob('**/**', path.join(cachePath))),
};
};

View File

@@ -0,0 +1,8 @@
use now_lambda::lambda;
use std::error::Error;
// PLACEHOLDER
fn main() -> Result<(), Box<dyn Error>> {
Ok(lambda!(handler))
}

View File

@@ -0,0 +1,22 @@
{
"name": "@now/rust",
"version": "0.0.3-canary.2",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/zeit/now-builders.git",
"directory": "packages/now-rust"
},
"files": [
"index.js",
"download-install-rust-toolchain.js",
"launcher.rs"
],
"dependencies": {
"@iarna/toml": "^2.2.1",
"execa": "^1.0.0",
"fs-extra": "^7.0.1",
"node-fetch": "^2.3.0",
"tar": "^4.4.8"
}
}

View File

@@ -0,0 +1,196 @@
//! Provides a Now Lambda oriented request and response body entity interface
use std::{borrow::Cow, ops::Deref, str};
use base64::display::Base64Display;
use serde::ser::{Error as SerError, Serialize, Serializer};
/// Representation of http request and response bodies as supported
/// by Zeit Now v2.
///
/// These come in three flavors
/// * `Empty` ( no body )
/// * `Text` ( text data )
/// * `Binary` ( binary data )
///
/// Body types can be `Deref` and `AsRef`'d into `[u8]` types much like the `hyper` crate
///
/// # Examples
///
/// Body types are inferred with `From` implementations.
///
/// ## Text
///
/// Types like `String`, `str` whose type reflects
/// text produce `Body::Text` variants
///
/// ```
/// assert!(match now_lambda::Body::from("text") {
/// now_lambda::Body::Text(_) => true,
/// _ => false
/// })
/// ```
///
/// ## Binary
///
/// Types like `Vec<u8>` and `&[u8]` whose types reflect raw bytes produce `Body::Binary` variants
///
/// ```
/// assert!(match now_lambda::Body::from("text".as_bytes()) {
/// now_lambda::Body::Binary(_) => true,
/// _ => false
/// })
/// ```
///
/// `Binary` responses bodies will automatically get base64 encoded.
///
/// ## Empty
///
/// The unit type ( `()` ) whose type represents an empty value produces `Body::Empty` variants
///
/// ```
/// assert!(match now_lambda::Body::from(()) {
/// now_lambda::Body::Empty => true,
/// _ => false
/// })
/// ```
#[derive(Debug, PartialEq)]
pub enum Body {
/// An empty body
Empty,
/// A body containing string data
Text(String),
/// A body containing binary data
Binary(Vec<u8>),
}
impl Default for Body {
fn default() -> Self {
Body::Empty
}
}
impl From<()> for Body {
fn from(_: ()) -> Self {
Body::Empty
}
}
impl From<Body> for () {
fn from(_: Body) -> Self {
()
}
}
impl<'a> From<&'a str> for Body {
fn from(s: &'a str) -> Self {
Body::Text(s.into())
}
}
impl From<String> for Body {
fn from(b: String) -> Self {
Body::Text(b)
}
}
impl From<Body> for String {
fn from(b: Body) -> String {
match b {
Body::Empty => String::from(""),
Body::Text(t) => t,
Body::Binary(b) => str::from_utf8(&b).unwrap().to_owned(),
}
}
}
impl From<Cow<'static, str>> for Body {
#[inline]
fn from(cow: Cow<'static, str>) -> Body {
match cow {
Cow::Borrowed(b) => Body::from(b.to_owned()),
Cow::Owned(o) => Body::from(o),
}
}
}
impl From<Body> for Cow<'static, str> {
#[inline]
fn from(b: Body) -> Cow<'static, str> {
Cow::Owned(String::from(b))
}
}
impl From<Cow<'static, [u8]>> for Body {
#[inline]
fn from(cow: Cow<'static, [u8]>) -> Body {
match cow {
Cow::Borrowed(b) => Body::from(b),
Cow::Owned(o) => Body::from(o),
}
}
}
impl From<Body> for Cow<'static, [u8]> {
#[inline]
fn from(b: Body) -> Self {
Cow::Owned(b.as_ref().to_owned())
}
}
impl From<Vec<u8>> for Body {
fn from(b: Vec<u8>) -> Self {
Body::Binary(b)
}
}
impl From<Body> for Vec<u8> {
fn from(b: Body) -> Self {
match b {
Body::Empty => "".as_bytes().to_owned(),
Body::Text(t) => t.into_bytes(),
Body::Binary(b) => b.to_owned(),
}
}
}
impl<'a> From<&'a [u8]> for Body {
fn from(b: &'a [u8]) -> Self {
Body::Binary(b.to_vec())
}
}
impl Deref for Body {
type Target = [u8];
#[inline]
fn deref(&self) -> &Self::Target {
self.as_ref()
}
}
impl AsRef<[u8]> for Body {
#[inline]
fn as_ref(&self) -> &[u8] {
match self {
Body::Empty => &[],
Body::Text(ref bytes) => bytes.as_ref(),
Body::Binary(ref bytes) => bytes.as_ref(),
}
}
}
impl<'a> Serialize for Body {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Body::Text(data) => serializer
.serialize_str(::std::str::from_utf8(data.as_ref()).map_err(S::Error::custom)?),
Body::Binary(data) => {
serializer.collect_str(&Base64Display::with_config(data, base64::STANDARD))
}
Body::Empty => serializer.serialize_unit(),
}
}
}

View File

@@ -0,0 +1,44 @@
use http;
use lambda_runtime::error::LambdaErrorExt;
use std::{error::Error, fmt};
/// This module implements a custom error currently over the AWS Lambda runtime,
/// which can be extended later to support more service providers.
#[derive(Debug)]
pub struct NowError {
msg: String,
}
impl NowError {
pub fn new(message: &str) -> NowError {
NowError {
msg: message.to_owned(),
}
}
}
impl fmt::Display for NowError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.msg)
}
}
impl Error for NowError {}
impl From<std::num::ParseIntError> for NowError {
fn from(i: std::num::ParseIntError) -> Self {
NowError::new(&format!("{}", i))
}
}
impl From<http::Error> for NowError {
fn from(i: http::Error) -> Self {
NowError::new(&format!("{}", i))
}
}
// the value returned by the error_type function is included as the
// `errorType` in the AWS Lambda response
impl LambdaErrorExt for NowError {
fn error_type(&self) -> &str {
"NowError"
}
}

View File

@@ -0,0 +1,91 @@
pub use http::{self, Response};
use lambda_runtime::{self as lambda, Context};
use log::{self, debug, error};
use serde_json::Error;
use tokio::runtime::Runtime as TokioRuntime;
mod body;
pub mod error;
pub mod request;
mod response;
mod strmap;
pub use crate::{body::Body, response::IntoResponse, strmap::StrMap};
use crate::{
error::NowError,
request::{NowEvent, NowRequest},
response::NowResponse,
};
/// Type alias for `http::Request`s with a fixed `now_lambda::Body` body
pub type Request = http::Request<Body>;
/// Functions acting as Now Lambda handlers must conform to this type.
pub trait Handler<R, B, E> {
/// Method to execute the handler function
fn run(&mut self, event: http::Request<B>) -> Result<R, E>;
}
impl<Function, R, B, E> Handler<R, B, E> for Function
where
Function: FnMut(http::Request<B>) -> Result<R, E>,
{
fn run(&mut self, event: http::Request<B>) -> Result<R, E> {
(*self)(event)
}
}
/// Creates a new `lambda_runtime::Runtime` and begins polling for Now Lambda events
///
/// # Arguments
///
/// * `f` A type that conforms to the `Handler` interface.
///
/// # Panics
/// The function panics if the Lambda environment variables are not set.
pub fn start<R, B, E>(f: impl Handler<R, B, E>, runtime: Option<TokioRuntime>)
where
B: From<Body>,
E: Into<NowError>,
R: IntoResponse,
{
// handler requires a mutable ref
let mut func = f;
lambda::start(
|e: NowEvent, _ctx: Context| {
let req_str = e.body;
let parse_result: Result<NowRequest, Error> = serde_json::from_str(&req_str);
match parse_result {
Ok(req) => {
debug!("Deserialized Now proxy request successfully");
let request: http::Request<Body> = req.into();
func.run(request.map(|b| b.into()))
.map(|resp| NowResponse::from(resp.into_response()))
.map_err(|e| e.into())
}
Err(e) => {
error!("Could not deserialize event body to NowRequest {}", e);
panic!("Could not deserialize event body to NowRequest {}", e);
}
}
},
runtime,
)
}
/// A macro for starting new handler's poll for Now Lambda events
#[macro_export]
macro_rules! lambda {
($handler:expr) => {
$crate::start($handler, None)
};
($handler:expr, $runtime:expr) => {
$crate::start($handler, Some($runtime))
};
($handler:ident) => {
$crate::start($handler, None)
};
($handler:ident, $runtime:expr) => {
$crate::start($handler, Some($runtime))
};
}

View File

@@ -0,0 +1,122 @@
use std::{borrow::Cow, fmt, mem};
use http::{self, header::HeaderValue, HeaderMap, Method, Request as HttpRequest};
use serde::de::{Deserializer, Error as DeError, MapAccess, Visitor};
use serde_derive::Deserialize;
use crate::body::Body;
/// Representation of a Now Lambda proxy event data
#[doc(hidden)]
#[derive(Deserialize, Debug, Default)]
#[serde(rename_all = "camelCase")]
pub(crate) struct NowRequest<'a> {
pub(crate) host: Cow<'a, str>,
pub(crate) path: Cow<'a, str>,
#[serde(deserialize_with = "deserialize_method")]
pub(crate) method: Method,
#[serde(deserialize_with = "deserialize_headers")]
pub(crate) headers: HeaderMap<HeaderValue>,
pub(crate) body: Option<Cow<'a, str>>,
pub(crate) encoding: Option<String>,
}
#[doc(hidden)]
#[derive(Deserialize, Debug, Default)]
pub(crate) struct NowEvent<'a> {
#[serde(rename = "Action")]
action: Cow<'a, str>,
pub(crate) body: Cow<'a, str>,
}
fn deserialize_method<'de, D>(deserializer: D) -> Result<Method, D::Error>
where
D: Deserializer<'de>,
{
struct MethodVisitor;
impl<'de> Visitor<'de> for MethodVisitor {
type Value = Method;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "a Method")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: DeError,
{
v.parse().map_err(E::custom)
}
}
deserializer.deserialize_str(MethodVisitor)
}
fn deserialize_headers<'de, D>(deserializer: D) -> Result<HeaderMap<HeaderValue>, D::Error>
where
D: Deserializer<'de>,
{
struct HeaderVisitor;
impl<'de> Visitor<'de> for HeaderVisitor {
type Value = HeaderMap<HeaderValue>;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "a HeaderMap<HeaderValue>")
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
let mut headers = http::HeaderMap::new();
while let Some((key, value)) = map.next_entry::<Cow<'_, str>, Cow<'_, str>>()? {
let header_name = key
.parse::<http::header::HeaderName>()
.map_err(A::Error::custom)?;
let header_value =
http::header::HeaderValue::from_shared(value.into_owned().into())
.map_err(A::Error::custom)?;
headers.append(header_name, header_value);
}
Ok(headers)
}
}
deserializer.deserialize_map(HeaderVisitor)
}
impl<'a> From<NowRequest<'a>> for HttpRequest<Body> {
fn from(value: NowRequest<'_>) -> Self {
let NowRequest {
host,
path,
method,
headers,
body,
encoding,
} = value;
// build an http::Request<now_lambda::Body> from a now_lambda::NowRequest
let mut builder = HttpRequest::builder();
builder.method(method);
builder.uri({ format!("https://{}{}", host, path) });
let mut req = builder
.body(match (body, encoding) {
(Some(ref b), Some(ref encoding)) if encoding == "base64" => {
// todo: document failure behavior
Body::from(::base64::decode(b.as_ref()).unwrap_or_default())
}
(Some(b), Some(_)) => Body::from(b.into_owned()),
_ => Body::from(()),
})
.expect("failed to build request");
// no builder method that sets headers in batch
mem::replace(req.headers_mut(), headers);
req
}
}

View File

@@ -0,0 +1,122 @@
//! Response types
use http::{
header::{HeaderMap, HeaderValue},
Response,
};
use serde::ser::{Error as SerError, SerializeMap, Serializer};
use serde_derive::Serialize;
use crate::body::Body;
/// Representation of a Now Lambda response
#[derive(Serialize, Debug)]
#[serde(rename_all = "camelCase")]
pub(crate) struct NowResponse {
pub status_code: u16,
#[serde(
skip_serializing_if = "HeaderMap::is_empty",
serialize_with = "serialize_headers"
)]
pub headers: HeaderMap<HeaderValue>,
#[serde(skip_serializing_if = "Option::is_none")]
pub body: Option<Body>,
#[serde(skip_serializing_if = "Option::is_none")]
pub encoding: Option<String>,
}
impl Default for NowResponse {
fn default() -> Self {
Self {
status_code: 200,
headers: Default::default(),
body: Default::default(),
encoding: Default::default(),
}
}
}
fn serialize_headers<S>(headers: &HeaderMap<HeaderValue>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(Some(headers.keys_len()))?;
for key in headers.keys() {
let map_value = headers[key].to_str().map_err(S::Error::custom)?;
map.serialize_entry(key.as_str(), map_value)?;
}
map.end()
}
impl<T> From<Response<T>> for NowResponse
where
T: Into<Body>,
{
fn from(value: Response<T>) -> Self {
let (parts, bod) = value.into_parts();
let (encoding, body) = match bod.into() {
Body::Empty => (None, None),
b @ Body::Text(_) => (None, Some(b)),
b @ Body::Binary(_) => (Some("base64".to_string()), Some(b)),
};
NowResponse {
status_code: parts.status.as_u16(),
body,
headers: parts.headers,
encoding,
}
}
}
/// A conversion of self into a `Response`
///
/// Implementations for `Response<B> where B: Into<Body>`,
/// `B where B: Into<Body>` and `serde_json::Value` are provided
/// by default
///
/// # example
///
/// ```rust
/// use now_lambda::{Body, IntoResponse, Response};
///
/// assert_eq!(
/// "hello".into_response().body(),
/// Response::new(Body::from("hello")).body()
/// );
/// ```
pub trait IntoResponse {
/// Return a translation of `self` into a `Response<Body>`
fn into_response(self) -> Response<Body>;
}
impl<B> IntoResponse for Response<B>
where
B: Into<Body>,
{
fn into_response(self) -> Response<Body> {
let (parts, body) = self.into_parts();
Response::from_parts(parts, body.into())
}
}
impl<B> IntoResponse for B
where
B: Into<Body>,
{
fn into_response(self) -> Response<Body> {
Response::new(self.into())
}
}
impl IntoResponse for serde_json::Value {
fn into_response(self) -> Response<Body> {
Response::builder()
.header(http::header::CONTENT_TYPE, "application/json")
.body(
serde_json::to_string(&self)
.expect("unable to serialize serde_json::Value")
.into(),
)
.expect("unable to build http::Response")
}
}

View File

@@ -0,0 +1,90 @@
use std::{
collections::{hash_map::Keys, HashMap},
fmt,
sync::Arc,
};
use serde::de::{Deserialize, Deserializer, MapAccess, Visitor};
/// A read-only view into a map of string data
#[derive(Default, Debug, PartialEq)]
pub struct StrMap(pub(crate) Arc<HashMap<String, String>>);
impl StrMap {
/// Return a named value where available
pub fn get(&self, key: &str) -> Option<&str> {
self.0.get(key).map(|value| value.as_ref())
}
/// Return true if the underlying map is empty
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
/// Return an iterator over keys and values
pub fn iter(&self) -> StrMapIter<'_> {
StrMapIter {
data: self,
keys: self.0.keys(),
}
}
}
impl Clone for StrMap {
fn clone(&self) -> Self {
// only clone the inner data
StrMap(self.0.clone())
}
}
impl From<HashMap<String, String>> for StrMap {
fn from(inner: HashMap<String, String>) -> Self {
StrMap(Arc::new(inner))
}
}
/// A read only reference to `StrMap` key and value slice pairings
pub struct StrMapIter<'a> {
data: &'a StrMap,
keys: Keys<'a, String, String>,
}
impl<'a> Iterator for StrMapIter<'a> {
type Item = (&'a str, &'a str);
#[inline]
fn next(&mut self) -> Option<(&'a str, &'a str)> {
self.keys
.next()
.and_then(|k| self.data.get(k).map(|v| (k.as_str(), v)))
}
}
impl<'de> Deserialize<'de> for StrMap {
fn deserialize<D>(deserializer: D) -> Result<StrMap, D::Error>
where
D: Deserializer<'de>,
{
struct StrMapVisitor;
impl<'de> Visitor<'de> for StrMapVisitor {
type Value = StrMap;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "a StrMap")
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
let mut inner = HashMap::new();
while let Some((key, value)) = map.next_entry()? {
inner.insert(key, value);
}
Ok(StrMap(Arc::new(inner)))
}
}
deserializer.deserialize_map(StrMapVisitor)
}
}

View File

@@ -1,11 +1,21 @@
const download = require('@now/build-utils/fs/download.js');
const glob = require('@now/build-utils/fs/glob.js');
const download = require('@now/build-utils/fs/download.js'); // eslint-disable-line import/no-extraneous-dependencies
const glob = require('@now/build-utils/fs/glob.js'); // eslint-disable-line import/no-extraneous-dependencies
const path = require('path');
const { existsSync } = require('fs');
const {
runNpmInstall,
runPackageJsonScript,
runShellScript,
} = require('@now/build-utils/fs/run-user-scripts.js');
} = require('@now/build-utils/fs/run-user-scripts.js'); // eslint-disable-line import/no-extraneous-dependencies
function validateDistDir(distDir) {
const distDirName = path.basename(distDir);
if (!existsSync(distDir)) {
const message = `Build was unable to create the distDir: ${distDirName}.`
+ '\nMake sure you mentioned the correct dist directory: https://zeit.co/docs/v2/deployments/official-builders/static-build-now-static-build/#configuring-the-dist-directory';
throw new Error(message);
}
}
exports.build = async ({
files, entrypoint, workPath, config,
@@ -24,6 +34,7 @@ exports.build = async ({
if (path.basename(entrypoint) === 'package.json') {
await runNpmInstall(entrypointFsDirname, ['--prefer-offline']);
if (await runPackageJsonScript(entrypointFsDirname, 'now-build')) {
validateDistDir(distPath);
return glob('**', distPath, mountpoint);
}
throw new Error(`An error running "now-build" script in "${entrypoint}"`);
@@ -31,6 +42,7 @@ exports.build = async ({
if (path.extname(entrypoint) === '.sh') {
await runShellScript(path.join(workPath, entrypoint));
validateDistDir(distPath);
return glob('**', distPath, mountpoint);
}

View File

@@ -1,9 +1,11 @@
{
"name": "@now/static-build",
"version": "0.4.17",
"version": "0.4.19-canary.1",
"license": "MIT",
"peerDependencies": {
"@now/build-utils": ">=0.0.1"
"repository": {
"type": "git",
"url": "https://github.com/zeit/now-builders.git",
"directory": "packages/now-static-build"
},
"scripts": {
"test": "jest"

View File

@@ -0,0 +1,6 @@
{
"version": 2,
"builds": [
{ "src": "package.json", "use": "@now/static-build", "config": {"distDir": "out"} }
]
}

View File

@@ -0,0 +1,8 @@
{
"dependencies": {
"cowsay": "^1.3.1"
},
"scripts": {
"now-build": "mkdir dist && cowsay cow:RANDOMNESS_PLACEHOLDER > dist/index.txt"
}
}

View File

@@ -0,0 +1,8 @@
{
"dependencies": {
"yodasay": "^1.1.6"
},
"scripts": {
"now-build": "mkdir dist && yodasay yoda:RANDOMNESS_PLACEHOLDER > dist/index.txt"
}
}

View File

@@ -7,7 +7,7 @@ const {
testDeployment,
} = require('../../../test/lib/deployment/test-deployment.js');
jest.setTimeout(2 * 60 * 1000);
jest.setTimeout(4 * 60 * 1000);
const buildUtilsUrl = '@canary';
let builderUrl;
@@ -21,6 +21,21 @@ const fixturesPath = path.resolve(__dirname, 'fixtures');
// eslint-disable-next-line no-restricted-syntax
for (const fixture of fs.readdirSync(fixturesPath)) {
if (fixture === '04-wrong-dist-dir') {
// eslint-disable-next-line no-loop-func
it(`should not build ${fixture}`, async () => {
try {
await testDeployment(
{ builderUrl, buildUtilsUrl },
path.join(fixturesPath, fixture),
);
} catch (err) {
expect(err.message).toMatch(/is ERROR/);
}
});
continue; //eslint-disable-line
}
// eslint-disable-next-line no-loop-func
it(`should build ${fixture}`, async () => {
await expect(

View File

@@ -1,11 +1,11 @@
const assert = require('assert');
const { createLambda } = require('@now/build-utils/lambda.js');
const { createLambda } = require('@now/build-utils/lambda.js'); // eslint-disable-line import/no-extraneous-dependencies
const fetch = require('node-fetch');
const FileBlob = require('@now/build-utils/file-blob.js');
const FileBlob = require('@now/build-utils/file-blob.js'); // eslint-disable-line import/no-extraneous-dependencies
const { getFiles } = require('@now/php-bridge');
const path = require('path');
const rename = require('@now/build-utils/fs/rename.js');
const streamToBuffer = require('@now/build-utils/fs/stream-to-buffer.js');
const rename = require('@now/build-utils/fs/rename.js'); // eslint-disable-line import/no-extraneous-dependencies
const streamToBuffer = require('@now/build-utils/fs/stream-to-buffer.js'); // eslint-disable-line import/no-extraneous-dependencies
const yauzl = require('yauzl');
exports.config = {
@@ -16,7 +16,9 @@ async function readReleaseUrl(releaseUrl) {
const resp = await fetch(releaseUrl);
if (!resp.ok) {
throw new Error(`Failed to download ${releaseUrl}. Status code is ${resp.status}`);
throw new Error(
`Failed to download ${releaseUrl}. Status code is ${resp.status}`,
);
}
return resp.buffer();
@@ -49,13 +51,15 @@ function decompressBuffer(buffer, mountpoint) {
return;
}
streamToBuffer(readStream).then((data) => {
assert(prefixRegexp.test(fileName), fileName);
const fileName2 = fileName.replace(prefixRegexp, '');
const fileName3 = path.join(mountpoint, fileName2);
files[fileName3] = new FileBlob({ data });
zipfile.readEntry();
}).catch(reject);
streamToBuffer(readStream)
.then((data) => {
assert(prefixRegexp.test(fileName), fileName);
const fileName2 = fileName.replace(prefixRegexp, '');
const fileName3 = path.join(mountpoint, fileName2);
files[fileName3] = new FileBlob({ data });
zipfile.readEntry();
})
.catch(reject);
});
});
@@ -65,12 +69,22 @@ function decompressBuffer(buffer, mountpoint) {
}
const staticRegexps = [
/\.css$/, /\.gif$/, /\.ico$/, /\.js$/, /\.jpg$/, /\.png$/, /\.svg$/, /\.woff$/, /\.woff2$/,
/\.css$/,
/\.gif$/,
/\.ico$/,
/\.js$/,
/\.jpg$/,
/\.png$/,
/\.svg$/,
/\.woff$/,
/\.woff2$/,
];
exports.build = async ({ files, entrypoint, config }) => {
if (path.basename(entrypoint) !== 'wp-config.php') {
throw new Error(`Entrypoint file name must be "wp-config.php". Currently it is ${entrypoint}`);
throw new Error(
`Entrypoint file name must be "wp-config.php". Currently it is ${entrypoint}`,
);
}
const { releaseUrl = 'https://wordpress.org/latest.zip' } = config;
@@ -84,9 +98,12 @@ exports.build = async ({ files, entrypoint, config }) => {
if (config.patchForPersistentConnections) {
const wpDbPhp = path.join(mountpoint, 'wp-includes/wp-db.php');
const wpDbPhpBlob = mergedFiles[wpDbPhp];
wpDbPhpBlob.data = wpDbPhpBlob.data.toString()
.replace(/mysqli_real_connect\( \$this->dbh, \$host,/g,
'mysqli_real_connect( $this->dbh, \'p:\' . $host,');
wpDbPhpBlob.data = wpDbPhpBlob.data
.toString()
.replace(
/mysqli_real_connect\( \$this->dbh, \$host,/g,
"mysqli_real_connect( $this->dbh, 'p:' . $host,",
);
}
const staticFiles = {};

View File

@@ -1,15 +1,17 @@
{
"name": "@now/wordpress",
"version": "0.4.14",
"version": "0.4.15-canary.0",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/zeit/now-builders.git",
"directory": "packages/now-wordpress"
},
"dependencies": {
"@now/php-bridge": "^0.4.13",
"@now/php-bridge": "^0.4.14-canary.0",
"node-fetch": "2.3.0",
"yauzl": "2.10.0"
},
"peerDependencies": {
"@now/build-utils": ">=0.0.1"
},
"scripts": {
"test": "jest"
}

View File

@@ -0,0 +1 @@
module.exports = () => 'Hello!';

View File

@@ -1,9 +1,9 @@
{
"name": "monorepo",
"dependencies": {
"next": "^8.0.0-canary.2",
"react": "^16.7.0",
"react-dom": "^16.7.0"
"next": "^8.0.0",
"react": "^16.8.0",
"react-dom": "^16.8.0"
},
"scripts": {
"now-build": "next build"

View File

@@ -1 +1,3 @@
export default () => 'Index page';
import hello from '../../shared/hello';
export default () => `${hello()} Welcome to the index page`;

View File

@@ -18,8 +18,9 @@ async function nowDeploy (bodies, randomness) {
const nowDeployPayload = {
version: 2,
env: Object.assign({}, nowJson.env, { RANDOMNESS_ENV_VAR: randomness }),
build: { env: { RANDOMNESS_BUILD_ENV_VAR: randomness } },
public: true,
env: { ...nowJson.env, RANDOMNESS_ENV_VAR: randomness },
build: { env: { ...(nowJson.build || {}).env, RANDOMNESS_BUILD_ENV_VAR: randomness } },
name: 'test',
files,
builds: nowJson.builds,

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