Compare commits

...

37 Commits

Author SHA1 Message Date
Igor Klopov
e83d4d4249 Publish
- @now/bash@0.0.3
 - @now/build-utils@0.4.31
 - @now/cgi@0.0.14
 - @now/go@0.2.11
 - @now/lambda@0.4.8
 - @now/md@0.4.8
 - @now/mdx-deck@0.4.17
 - @now/next@0.0.81
 - @now/node-server@0.4.25
 - @now/node@0.4.27
 - @now/php@0.4.12
 - @now/static-build@0.4.16
2018-12-06 01:54:09 +03:00
Igor Klopov
d2ca763079 update yarn.lock 2018-12-06 01:52:54 +03:00
Nathan Rajlich
2a95388f89 Publish
- @now/bash@0.0.3-canary.0
 - @now/build-utils@0.4.31-canary.2
 - @now/cgi@0.0.14-canary.0
 - @now/go@0.2.11-canary.1
 - @now/lambda@0.4.8-canary.1
 - @now/md@0.4.8-canary.2
 - @now/mdx-deck@0.4.17-canary.3
 - @now/node-server@0.4.25-canary.3
 - @now/node@0.4.27-canary.3
 - @now/php@0.4.12-canary.3
 - @now/static-build@0.4.16-canary.2
2018-12-04 17:40:38 -08:00
Nathan Rajlich
be9fedfdc4 Bump ncc to v0.4.1 (#117) 2018-12-04 17:38:41 -08:00
Igor Klopov
f0dee65f69 fixes for now/php 2018-12-05 00:46:59 +03:00
Daniel Levine
5514753c07 Update index.js (#115)
quick typo fix
2018-12-04 13:12:58 -08:00
Nathan Rajlich
7028556919 Add @now/bash builder (#114)
This builder expects a `.sh` shell script entrypoint file, and implements
a custom Lambda runtime written in bash.

* The entrypoint must define a `serve()` function which is executed for every
  HTTP request.
* The entrypoint _may_ define a `build()` function only gets executed
  during the build, to retreive any additional artifacts that the lambda
  depends on.
* `jq` is available implicitly, since the runtime depends on it to parse the
  event and generate the response.
* Stdin is the HTTP request body
* Stdout is the HTTP response body
* Invoke `http_response_code` to set the response status code
* Invoke `http_response_header` to set a HTTP response header
* Parse the `$REQUEST` variable with `jq` to get context about the request
  (path, method, etc.)
* [`import`](https://import.pw) is available by default, and `import`
  calls at the top-level of your file will be cached inside the lambda at
  build-time
* Since the runtime itself is written in bash, every time `serve` is
  invoked, _it is the same process_. This means that you can use global
  state in your script, for example:

```bash
COUNT=0

serve() {
  COUNT="$(( COUNT + 1 ))"
  echo "Invoke count: $COUNT"
}
```
2018-12-04 22:50:11 +03:00
Nathan Rajlich
a1f24853fc Create go/bridge common logic package for Go lambda builders (#96)
* Create `go/bridge` common logic package for Go lambda builders

* gofmt

* Fill out more properties on the `http.Request`
2018-12-04 22:47:09 +03:00
Igor Klopov
9804e82f8f add top-level package.json dummy 'version' entry 2018-12-04 07:28:00 +03:00
Igor Klopov
e96596634b specify '@canary' explicitly as builderUrl or buildUtilsUrl 2018-12-04 02:52:05 +03:00
Igor Klopov
495c193a40 Publish
- @now/build-utils@0.4.31-canary.1
 - @now/lambda@0.4.8-canary.0
 - @now/md@0.4.8-canary.1
 - @now/mdx-deck@0.4.17-canary.2
 - @now/node-server@0.4.25-canary.2
 - @now/node@0.4.27-canary.2
 - @now/php@0.4.12-canary.2
 - @now/static-build@0.4.16-canary.1
2018-12-04 02:03:45 +03:00
Igor Klopov
9d77d89513 bump ncc to 0.3.0 (#113) 2018-12-04 02:00:33 +03:00
Igor Klopov
bcffe0db91 take random token from pool if CIRCLE_BUILD_NUM is absent 2018-12-04 01:02:21 +03:00
Igor Klopov
228892fe9f pass API_HOST from process.env, don't write auth.json 2018-12-04 00:29:46 +03:00
Igor Klopov
c7d434c81d More tests 4 (#112)
* replace RANDOMNESS_PLACEHOLDER in all files

* [wip] test for now/lambda

* fix for lambda test

* testDeployment to return { deploymentId, deploymentUrl }
2018-12-04 00:03:25 +03:00
Igor Klopov
66bf4aa25a Publish
- @now/md@0.4.8-canary.0
 - @now/mdx-deck@0.4.17-canary.1
 - @now/next@0.0.81-canary.1
 - @now/node-server@0.4.25-canary.1
 - @now/node@0.4.27-canary.1
 - @now/php@0.4.12-canary.1
 - @now/static-build@0.4.16-canary.0
2018-12-03 04:54:10 +03:00
Igor Klopov
f328a53a3f bump ncc to 0.2.0 (#110) 2018-12-03 04:51:20 +03:00
Tim Neutkens
168b4cf2bb Include /static directory for now/next (#109) 2018-12-02 18:40:13 +01:00
Thomas Lindstrøm
a91e3da7d8 Support default export from import/export syntax (#30)
Fix issue where files using new `import/export` syntax ran into 502 when also exporting other named exports.
https://spectrum.chat/thread/4322e805-b8ba-43d2-96e2-98b2497724e3

```
const data = {};

export default function handler(request, response) {
  response.end('hello');
}

export {data};
```
2018-11-30 08:17:07 +03:00
Igor Klopov
756c452203 bump ncc in node-server and node 2018-11-30 07:59:11 +03:00
Igor Klopov
80db1937f8 take pool position from CIRCLE_BUILD_NUM 2018-11-29 05:35:45 +03:00
Igor Klopov
d9df4b0929 pick auth token from pool 2018-11-29 05:10:47 +03:00
Igor Klopov
08c52ebbf2 More tests 3 (#101)
* test for now/md

* yarn.lock is not needed in apollo anymore

* test for now/mdx
2018-11-29 04:39:29 +03:00
Tim Neutkens
fe8ead391a Publish
- @now/next@0.0.81-canary.0
2018-11-28 19:21:44 +01:00
Tim Neutkens
5d45f2df31 @now/next Fix prepareCache (#98) 2018-11-28 19:19:01 +01:00
Nathan Rajlich
1385c1ca48 Add yarn.lock file to root 2018-11-27 16:10:36 -08:00
Nathan Rajlich
4c63009123 Add manual publish instructions to README 2018-11-27 16:10:18 -08:00
Nathan Rajlich
389c85cef5 Publish
- @now/go@0.2.11-canary.0
 - @now/mdx-deck@0.4.17-canary.0
2018-11-27 16:06:37 -08:00
Vince Picone
227c239bbc [now/mdx-deck] update mdx-deck to v1.7.15 (#86)
* Update mdx-deck dependancy

Builds failing due to a breaking change in babel-plugin-styled-components 1.9.0. Updates mdx-deck to a version with babel-plugin-styled-components pinned to 1.8.0.

https://github.com/jxnblk/mdx-deck/issues/228

Related: https://github.com/styled-components/babel-plugin-styled-components/issues/182

* Only changed version in one place.
2018-11-27 15:58:10 -08:00
Nathan Rajlich
b93736132a [now-go] always send the response as base64 (#85)
This is in-line with the CGI and PHP (#83) builders.

(Side note: we really need to make a common go import for this logic…)
2018-11-27 15:51:05 -08:00
Igor Klopov
26849422ff Publish
- @now/build-utils@0.4.31-canary.0
 - @now/node-server@0.4.25-canary.0
 - @now/node@0.4.27-canary.0
2018-11-28 02:18:45 +03:00
Igor Klopov
31dd1ca491 build-utils: inject memory-fs for yarn cache (#91)
* build-utils: inject memory-fs for yarn cache

* ditch shelljs dependency

* try to cover circle ci odd yarn installation

* Revert "try to cover circle ci odd yarn installation"

This reverts commit 751949a5dd209bab34934042eba3be1904a86ac9.

* use in-memory cache for aws only

* separate isInsideCachePath function

* print 'using memory-fs for yarn cache'

* wrong name. isInsideCachePath -> isOutsideCachePath
2018-11-27 22:12:37 +03:00
Igor Klopov
04a1ce89b3 bump ncc in node-server and node 2018-11-27 22:00:56 +03:00
Igor Klopov
3e247fd16d Tests for ncc assets (#82)
* [wip] added a test for ncc assets

* bump ncc, add 08-assets test to node-server as well

* builder-utils: don't run all fixtures, just some
2018-11-27 05:58:26 +03:00
Nathan Rajlich
9e127421ad Publish
- @now/php@0.4.12-canary.0
2018-11-26 16:51:13 -08:00
Nathan Rajlich
337d0cb1ed [now-php] set the proper response status code (#84)
1. The initial status code should be 200 (so that the first PHP call to `http_status_code` returns 200, as per the PHP documentation).
1. Extract the resulting status code and return it in the response.
2018-11-26 16:47:44 -08:00
Nathan Rajlich
ad3cdf46f4 [now-php] always send the response as base64 (#83)
This should address https://spectrum.chat/zeit/general/corrupted-image-using-php~9fbb8cc5-b210-49d1-8934-b393a628219f
2018-11-26 16:34:21 -08:00
84 changed files with 9758 additions and 1402 deletions

View File

@@ -19,3 +19,9 @@ For the canary channel use:
```
yarn publish-canary
```
CircleCI will take care of publishing the updated packages to npm from there.
If for some reason CircleCI fails to publish the npm package, you may do so
manually by running `npm publish` from the package directory. Make sure to
include the `--tag canary` parameter if you are publishing a canary release!

View File

@@ -1,5 +1,6 @@
{
"name": "now-builders",
"version": "0.0.0",
"private": true,
"license": "MIT",
"dependencies": {
@@ -30,6 +31,7 @@
"@types/glob": "^7.1.1",
"@types/multistream": "^2.1.1",
"@types/node": "^10.12.8",
"buffer-replace": "^1.0.0",
"eslint": "^5.9.0",
"eslint-config-airbnb-base": "^13.1.0",
"eslint-config-prettier": "^3.1.0",

2
packages/now-bash/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
node_modules
handler

14
packages/now-bash/bootstrap Executable file
View File

@@ -0,0 +1,14 @@
#!/bin/bash
set -euo pipefail
cd "$LAMBDA_TASK_ROOT"
# Configure `import`
export IMPORT_CACHE="$LAMBDA_TASK_ROOT/.import-cache"
export PATH="$IMPORT_CACHE/bin:$PATH"
# Load `import` and runtime
. "$(which import)"
. "$IMPORT_CACHE/runtime.sh"
# Load user code and process events in a loop forever
_lambda_runtime_init

49
packages/now-bash/builder.sh Executable file
View File

@@ -0,0 +1,49 @@
#!/bin/bash
set -euo pipefail
# `import` debug logs are always enabled during build
export IMPORT_DEBUG=1
# Install `import`
IMPORT_BIN="$IMPORT_CACHE/bin/import"
mkdir -p "$(dirname "$IMPORT_BIN")"
curl -sfLS https://import.pw > "$IMPORT_BIN"
chmod +x "$IMPORT_BIN"
# For now only the entrypoint file is copied into the lambda
mkdir -p "$(dirname "$ENTRYPOINT")"
cp "$SRC/$ENTRYPOINT" "$ENTRYPOINT"
# Copy in the runtime
cp "$BUILDER/runtime.sh" "$IMPORT_CACHE"
cp "$BUILDER/bootstrap" .
# Load `import`
. "$(which import)"
# Cache runtime and user dependencies
echo "Caching imports in \"$ENTRYPOINT\"…"
. "$IMPORT_CACHE/runtime.sh"
. "$ENTRYPOINT"
echo "Done caching imports"
# Run user build script
if declare -f build > /dev/null; then
echo "Running \`build\` function in \"$ENTRYPOINT\"…"
build "$@"
fi
# Ensure the entrypoint defined a `serve` function
if ! declare -f serve > /dev/null; then
echo "ERROR: A \`serve\` function must be defined in \"$ENTRYPOINT\"!" >&2
exit 1
fi
# Show a tree of the final lambda build
show_tree() {
import "static-binaries@0.0.6"
static_binaries tree
echo "Final lambda file tree:"
tree -a .
}
IMPORT_DEBUG= IMPORT_CACHE="$(mktemp -d)" show_tree

View File

@@ -0,0 +1,55 @@
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');
exports.config = {
maxLambdaSize: '10mb',
};
exports.analyze = ({ files, entrypoint }) => files[entrypoint].digest;
exports.build = async ({ files, entrypoint, config }) => {
const srcDir = await getWritableDirectory();
const workDir = await getWritableDirectory();
console.log('downloading files...');
await download(files, srcDir);
const configEnv = Object.keys(config).reduce((o, v) => {
o[`IMPORT_${snakeCase(v).toUpperCase()}`] = config[v]; // eslint-disable-line no-param-reassign
return o;
}, {});
const IMPORT_CACHE = `${workDir}/.import-cache`;
const env = Object.assign({}, process.env, configEnv, {
PATH: `${IMPORT_CACHE}/bin:${process.env.PATH}`,
IMPORT_CACHE,
SRC: srcDir,
BUILDER: __dirname,
ENTRYPOINT: entrypoint,
});
const builderPath = join(__dirname, 'builder.sh');
await execa(builderPath, [entrypoint], {
env,
cwd: workDir,
});
const lambda = await createLambda({
files: await glob('**', workDir),
handler: entrypoint, // not actually used in `bootstrap`
runtime: 'provided',
environment: Object.assign({}, configEnv, {
SCRIPT_FILENAME: entrypoint,
}),
});
return {
[entrypoint]: lambda,
};
};

View File

@@ -0,0 +1,22 @@
{
"name": "@now/bash",
"version": "0.0.3",
"description": "Now 2.0 builder for HTTP endpoints written in Bash",
"main": "index.js",
"author": "Nathan Rajlich <nate@zeit.co>",
"license": "MIT",
"files": [
"builder.sh",
"runtime.sh",
"bootstrap",
"index.js",
"package.json"
],
"dependencies": {
"execa": "^1.0.0",
"snake-case": "^2.1.0"
},
"peerDependencies": {
"@now/build-utils": ">=0.0.1"
}
}

View File

@@ -0,0 +1,115 @@
import "static-binaries@0.0.6"
static_binaries jq
# These get reset upon each request
_STATUS_CODE="$(mktemp)"
_HEADERS="$(mktemp)"
_lambda_runtime_api() {
local endpoint="$1"
shift
curl -sfLS "http://$AWS_LAMBDA_RUNTIME_API/2018-06-01/runtime/$endpoint" "$@"
}
_lambda_runtime_init() {
# Initialize user code
. "$SCRIPT_FILENAME" || {
local exit_code="$?"
local error
error='{"exitCode":'"$exit_code"'}'
_lambda_runtime_api "init/error" -X POST -d "$error"
exit "$EXIT_CODE"
}
# Process events
while true; do _lambda_runtime_next; done
}
_lambda_runtime_next() {
echo 200 > "$_STATUS_CODE"
echo '{"content-type":"text/plain; charset=utf8"}' > "$_HEADERS"
local headers
headers="$(mktemp)"
# Get an event
local event
event="$(mktemp)"
_lambda_runtime_api invocation/next -D "$headers" | jq -r '.body' > "$event"
local request_id
request_id="$(grep -Fi Lambda-Runtime-Aws-Request-Id "$headers" | tr -d '[:space:]' | cut -d: -f2)"
echo "Request-Id: $request_id" >&2
rm -f "$headers"
# Execute the handler function from the script
local body
body="$(mktemp)"
local exit_code=0
REQUEST="$event"
# Stdin of the `serve` function is the HTTP request body.
# 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)"
mkfifo "$stdin"
_lambda_runtime_body "$event" > "$stdin" &
serve "$event" < "$stdin" > "$body" || exit_code="$?"
rm -f "$event" "$stdin"
if [ "$exit_code" -eq 0 ]; then
# Send the response
local response
response="$(jq -cnMr \
--arg statusCode "$(cat "$_STATUS_CODE")" \
--argjson headers "$(cat "$_HEADERS")" \
--arg body "$(base64 --wrap=0 < "$body")" \
'{statusCode:$statusCode|tonumber, headers:$headers, encoding:"base64", body:$body}')"
rm -f "$body" "$_HEADERS"
_lambda_runtime_api "invocation/$request_id/response" -X POST -d "$response"
else
local error
error='{"exitCode":'"$exit_code"'}'
_lambda_runtime_api "invocation/$request_id/error" -X POST -d "$error"
fi
}
_lambda_runtime_body() {
if [ "$(jq -r '.body | type' < "$1")" = "string" ]; then
if [ "$(jq -r '.encoding' < "$1")" = "base64" ]; then
jq -r '.body' < "$1" | base64 -d
else
# assume plain-text body
jq -r '.body' < "$1"
fi
fi
}
# Set the response status code.
http_response_code() {
echo "$1" > "$_STATUS_CODE"
}
# Sets a response header.
# Overrides existing header if it has already been set.
http_response_header() {
local name="$1"
local value="$2"
local tmp
tmp="$(mktemp)"
jq --arg name "$name" --arg value "$value" '.[$name] = $value' < "$_HEADERS" > "$tmp"
mv -f "$tmp" "$_HEADERS"
}
http_response_redirect() {
http_response_code "${2:-302}"
http_response_header "location" "$1"
}
http_response_json() {
http_response_header "content-type" "application/json; charset=utf8"
}

146
packages/now-bash/yarn.lock Normal file
View File

@@ -0,0 +1,146 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
cross-spawn@^6.0.0:
version "6.0.5"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==
dependencies:
nice-try "^1.0.4"
path-key "^2.0.1"
semver "^5.5.0"
shebang-command "^1.2.0"
which "^1.2.9"
end-of-stream@^1.1.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43"
integrity sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==
dependencies:
once "^1.4.0"
execa@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8"
integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==
dependencies:
cross-spawn "^6.0.0"
get-stream "^4.0.0"
is-stream "^1.1.0"
npm-run-path "^2.0.0"
p-finally "^1.0.0"
signal-exit "^3.0.0"
strip-eof "^1.0.0"
get-stream@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5"
integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==
dependencies:
pump "^3.0.0"
is-stream@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
isexe@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
lower-case@^1.1.1:
version "1.1.4"
resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac"
integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw=
nice-try@^1.0.4:
version "1.0.5"
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
no-case@^2.2.0:
version "2.3.2"
resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac"
integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==
dependencies:
lower-case "^1.1.1"
npm-run-path@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=
dependencies:
path-key "^2.0.0"
once@^1.3.1, once@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
dependencies:
wrappy "1"
p-finally@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=
path-key@^2.0.0, path-key@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=
pump@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
dependencies:
end-of-stream "^1.1.0"
once "^1.3.1"
semver@^5.5.0:
version "5.6.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004"
integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==
shebang-command@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=
dependencies:
shebang-regex "^1.0.0"
shebang-regex@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=
signal-exit@^3.0.0:
version "3.0.2"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=
snake-case@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-2.1.0.tgz#41bdb1b73f30ec66a04d4e2cad1b76387d4d6d9f"
integrity sha1-Qb2xtz8w7GagTU4srRt2OH1NbZ8=
dependencies:
no-case "^2.2.0"
strip-eof@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=
which@^1.2.9:
version "1.3.1"
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
dependencies:
isexe "^2.0.0"
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=

View File

@@ -0,0 +1,83 @@
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 isOutsideCachePath(filename) {
const relative = path.relative(cachePath, filename);
return relative.startsWith('..');
}
const saveCreateWriteStream = fs.createWriteStream;
fs.createWriteStream = (...args) => {
const filename = args[0];
if (isOutsideCachePath(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 (isOutsideCachePath(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];
if (isOutsideCachePath(src)) {
return saveCopyFile.call(fs, ...args);
}
const dest = args[1];
const callback = args[args.length - 1];
const buffer = vfs.readFileSync(src);
return fs.writeFile(dest, buffer, callback);
};
const saveWriteFile = fs.writeFile;
fs.writeFile = (...args) => {
const filename = args[0];
if (isOutsideCachePath(filename)) {
return saveWriteFile.call(fs, ...args);
}
return vfs.writeFile(...args);
};
require(yarnPath);

View File

@@ -63,6 +63,15 @@ async function runNpmInstall(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 (process.env.AWS_EXECUTION_ENV) {
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,6 +1,6 @@
{
"name": "@now/build-utils",
"version": "0.4.30",
"version": "0.4.31",
"dependencies": {
"async-retry": "1.2.3",
"async-sema": "2.1.4",
@@ -8,6 +8,7 @@
"fs-extra": "7.0.0",
"glob": "7.1.3",
"into-stream": "4.0.0",
"memory-fs": "0.4.1",
"multistream": "2.1.1",
"node-fetch": "2.2.0",
"yazl": "2.4.3"

View File

@@ -8,6 +8,7 @@ const {
} = require('../../../test/lib/deployment/test-deployment.js');
jest.setTimeout(2 * 60 * 1000);
const builderUrl = '@canary';
let buildUtilsUrl;
beforeAll(async () => {
@@ -25,8 +26,11 @@ for (const fixture of fs.readdirSync(fixturesPath)) {
// eslint-disable-next-line no-loop-func
it(`should build ${fixture}`, async () => {
await expect(
testDeployment({ buildUtilsUrl }, path.join(fixturesPath, fixture)),
).resolves.toBe(undefined);
testDeployment(
{ builderUrl, buildUtilsUrl },
path.join(fixturesPath, fixture),
),
).resolves.toBeDefined();
});
}
@@ -43,11 +47,17 @@ for (const builder of buildersToTestWith) {
// eslint-disable-next-line no-restricted-syntax
for (const fixture of fs.readdirSync(fixturesPath2)) {
// eslint-disable-next-line no-loop-func
it(`should build ${builder}/${fixture}`, async () => {
await expect(
testDeployment({ buildUtilsUrl }, path.join(fixturesPath2, fixture)),
).resolves.toBe(undefined);
});
// don't run all foreign fixtures, just some
if (['01-cowsay', '03-env-vars'].includes(fixture)) {
// eslint-disable-next-line no-loop-func
it(`should build ${builder}/${fixture}`, async () => {
await expect(
testDeployment(
{ builderUrl, buildUtilsUrl },
path.join(fixturesPath2, fixture),
),
).resolves.toBeDefined();
});
}
}
}

View File

@@ -1,152 +1,36 @@
package main
import (
"os"
"fmt"
"net"
"strings"
"io/ioutil"
now "../../utils/go/bridge"
"net/http"
"net/http/cgi"
"os"
"path/filepath"
"encoding/json"
b64 "encoding/base64"
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
)
type Request struct {
Host string `json:"host"`
Path string `json:"path"`
Method string `json:"method"`
Headers map[string]string `json:"headers"`
Encoding string `json:"encoding,omitempty"`
Body string `json:"body"`
}
type Response struct {
StatusCode int `json:"statusCode"`
Headers map[string]string `json:"headers"`
Encoding string `json:"encoding,omitemtpy"`
Body string `json:"body"`
}
type ResponseError struct {
Code string `json:"code"`
Message string `json:"message"`
}
type ResponseErrorWrapper struct {
Error ResponseError `json:"error"`
}
type CgiHandler struct {
http.Handler
Dir string
Script string
}
func createErrorResponse(message string, code string, statusCode int) (Response, error) {
obj := ResponseErrorWrapper{
Error: ResponseError{
Code: code,
Message: message,
},
}
body, _ := json.Marshal(obj)
return Response{
StatusCode: statusCode,
Headers: map[string]string{
"Content-Type": "application/json",
},
Body: string(body),
}, nil
}
func (h *CgiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
cgih := cgi.Handler{
handler := cgi.Handler{
Path: h.Script,
Root: "/" + h.Script,
Dir: h.Dir,
Env: []string{"SERVER_PORT=443", "HTTPS=on", "SERVER_SOFTWARE=@now/cgi"},
Dir: h.Dir,
Env: []string{
"HTTPS=on",
"SERVER_PORT=443",
"SERVER_SOFTWARE=@now/cgi",
},
}
cgih.ServeHTTP(w, r)
handler.ServeHTTP(w, r)
}
func main() {
l, err := net.Listen("tcp", ":0")
if err != nil {
panic(err)
}
workdir, _ := filepath.Abs(".")
script := os.Getenv("SCRIPT_FILENAME")
h := &CgiHandler{nil, workdir, script}
http.Handle("/", h)
go http.Serve(l, nil)
handler := func(_req events.APIGatewayProxyRequest) (Response, error) {
var req Request
err := json.Unmarshal([]byte(_req.Body), &req)
if err != nil {
fmt.Println(err)
return createErrorResponse("Invalid payload", "bad_request", 400)
}
if req.Encoding == "base64" {
decoded, _ := b64.StdEncoding.DecodeString(req.Body)
req.Body = string(decoded)
}
url := "http://" + l.Addr().String() + req.Path
internalReq, err := http.NewRequest(req.Method, url, strings.NewReader(req.Body))
if err != nil {
fmt.Println(err)
return createErrorResponse("Bad gateway internal req failed", "bad_gateway", 502)
}
for k, v := range req.Headers {
internalReq.Header.Add(k, v)
if strings.ToLower(k) == "host" {
internalReq.Host = v
}
}
client := &http.Client{}
internalRes, err := client.Do(internalReq)
if err != nil {
fmt.Println(err)
return createErrorResponse("Bad gateway internal req Do failed", "bad_gateway", 502)
}
defer internalRes.Body.Close()
resHeaders := make(map[string]string, len(internalRes.Header))
for k, v := range internalRes.Header {
// FIXME: support multiple values via concatenating with ','
// see RFC 7230, section 3.2.2
resHeaders[k] = v[0]
}
bodyBytes, err := ioutil.ReadAll(internalRes.Body)
if err != nil {
return createErrorResponse("Bad gateway ReadAll bytes from response failed", "bad_gateway", 502)
}
resBody := b64.StdEncoding.EncodeToString(bodyBytes)
return Response{
StatusCode: internalRes.StatusCode,
Headers: resHeaders,
Encoding: "base64",
Body: resBody,
}, nil
}
lambda.Start(handler)
handler := &CgiHandler{nil, workdir, script}
now.Start(handler)
}

View File

@@ -1,6 +1,6 @@
{
"name": "@now/cgi",
"version": "0.0.13",
"version": "0.0.14",
"scripts": {
"test": "best -I test/*.js",
"prepublish": "./build.sh"

View File

@@ -1,3 +1,4 @@
node_modules
*.log
launcher
bin

View File

@@ -9,7 +9,7 @@ const downloadGit = require('lambda-git');
const glob = require('@now/build-utils/fs/glob.js');
const downloadGoBin = require('./download-go-bin');
// creates a `$GOPATH` direcotry tree, as per
// creates a `$GOPATH` directory tree, as per
// `go help gopath`'s instructions.
// without this, Go won't recognize the `$GOPATH`
async function createGoPathTree(goPath) {

View File

@@ -1,143 +1,10 @@
package main
import (
b64 "encoding/base64"
"encoding/json"
"fmt"
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
"io/ioutil"
"net"
now "../../utils/go/bridge"
"net/http"
"strings"
)
type Request struct {
Host string `json:"host"`
Path string `json:"path"`
Method string `json`
Headers map[string]string `json:"headers"`
Encoding string `json"encoding,omitempty"`
Body string `json:"body"`
}
type Response struct {
StatusCode int `json:"statusCode"`
Headers map[string]string `json:"headers"`
Encoding string `json:"encoding,omitemtpy"`
Body string `json:"body"`
}
type ResponseError struct {
Code string `json:"code"`
Message string `json:"message"`
}
type ResponseErrorWrapper struct {
Error ResponseError `json:"error"`
}
func createErrorResponse(message string, code string, statusCode int) (Response, error) {
obj := ResponseErrorWrapper{
Error: ResponseError{
Code: code,
Message: message,
},
}
body, _ := json.Marshal(obj)
return Response{
StatusCode: statusCode,
Headers: map[string]string{
"Content-Type": "application/json",
},
Body: string(body),
}, nil
}
func main() {
l, err := net.Listen("tcp", ":0")
if err != nil {
panic(err)
}
http.HandleFunc("/", __NOW_HANDLER_FUNC_NAME)
go http.Serve(l, nil)
handler := func(_req events.APIGatewayProxyRequest) (Response, error) {
var req Request
err := json.Unmarshal([]byte(_req.Body), &req)
if err != nil {
fmt.Println(err)
return createErrorResponse("Invalid payload", "bad_request", 400)
}
if req.Encoding == "base64" {
decoded, _ := b64.StdEncoding.DecodeString(req.Body)
req.Body = string(decoded)
}
url := "http://" + l.Addr().String() + req.Path
internalReq, err := http.NewRequest(req.Method, url, strings.NewReader(req.Body))
if err != nil {
fmt.Println(err)
return createErrorResponse("Bad gateway", "bad_gateway", 502)
}
for k, v := range req.Headers {
internalReq.Header.Add(k, v)
if strings.ToLower(k) == "host" {
// we need to set `Host` in the request
// because Go likes to ignore the `Host` header
// see https://github.com/golang/go/issues/7682
internalReq.Host = v
}
}
client := &http.Client{}
internalRes, err := client.Do(internalReq)
if err != nil {
fmt.Println(err)
return createErrorResponse("Bad gateway", "bad_gateway", 502)
}
defer internalRes.Body.Close()
resHeaders := make(map[string]string, len(internalRes.Header))
var resEncoding string
for k, v := range internalRes.Header {
// FIXME: support multiple values via concatenating with ','
// see RFC 7230, section 3.2.2
if strings.ToLower(k) == "x-now-response-encoding" {
// we don't want to send this header down
resEncoding = v[0]
} else {
resHeaders[k] = v[0]
}
}
bodyBytes, err := ioutil.ReadAll(internalRes.Body)
if err != nil {
return createErrorResponse("Bad gateway", "bad_gateway", 502)
}
var resBody string
if resEncoding == "base64" {
resBody = b64.StdEncoding.EncodeToString(bodyBytes)
} else {
resBody = string(bodyBytes)
}
return Response{
StatusCode: internalRes.StatusCode,
Headers: resHeaders,
Encoding: resEncoding,
Body: resBody,
}, nil
}
lambda.Start(handler)
now.Start(http.HandlerFunc(__NOW_HANDLER_FUNC_NAME))
}

View File

@@ -1,6 +1,6 @@
{
"name": "@now/go",
"version": "0.2.10",
"version": "0.2.11",
"scripts": {
"test": "best -I test/*.js",
"prepublish": "./build.sh"

View File

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

View File

@@ -1,7 +1,10 @@
{
"name": "@now/lambda",
"version": "0.4.7",
"version": "0.4.8",
"peerDependencies": {
"@now/build-utils": ">=0.0.1"
},
"scripts": {
"test": "jest"
}
}

Binary file not shown.

View File

@@ -0,0 +1,11 @@
{
"version": 2,
"builds": [
{ "src": "index.zip", "use": "@now/lambda", "config": { "handler": "index.handler", "runtime": "nodejs8.10" } },
{ "src": "subdirectory/index.zip", "use": "@now/lambda", "config": { "handler": "index.handler", "runtime": "nodejs8.10" } }
],
"probes": [
{ "path": "/", "mustContain": "cow:NO_REPLACE_TO_AVOID_CRC_MISMATCH" },
{ "path": "/subdirectory/", "mustContain": "yoda:NO_REPLACE_TO_AVOID_CRC_MISMATCH" }
]
}

Binary file not shown.

View File

@@ -0,0 +1,33 @@
/* global beforeAll, expect, it, jest */
const fs = require('fs');
const path = require('path');
const {
packAndDeploy,
testDeployment,
} = require('../../../test/lib/deployment/test-deployment.js');
jest.setTimeout(2 * 60 * 1000);
const buildUtilsUrl = '@canary';
let builderUrl;
beforeAll(async () => {
const builderPath = path.resolve(__dirname, '..');
builderUrl = await packAndDeploy(builderPath);
console.log('builderUrl', builderUrl);
});
const fixturesPath = path.resolve(__dirname, 'fixtures');
// eslint-disable-next-line no-restricted-syntax
for (const fixture of fs.readdirSync(fixturesPath)) {
// eslint-disable-next-line no-loop-func
it(`should build ${fixture}`, async () => {
await expect(
testDeployment(
{ builderUrl, buildUtilsUrl },
path.join(fixturesPath, fixture),
),
).resolves.toBeDefined();
});
}

View File

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

View File

@@ -1,6 +1,6 @@
{
"name": "@now/md",
"version": "0.4.7",
"version": "0.4.8",
"dependencies": {
"rehype-document": "^2.2.0",
"rehype-format": "^2.3.0",
@@ -12,5 +12,8 @@
},
"peerDependencies": {
"@now/build-utils": ">=0.0.1"
},
"scripts": {
"test": "jest"
}
}

View File

@@ -0,0 +1,5 @@
# Testing Markdown
cow:RANDOMNESS_PLACEHOLDER
[Wow a link!](https://zeit.co)

View File

@@ -0,0 +1,11 @@
{
"version": 2,
"builds": [
{ "src": "index.md", "use": "@now/md" },
{ "src": "subdirectory/index.md", "use": "@now/md" }
],
"probes": [
{ "path": "/", "mustContain": "cow:RANDOMNESS_PLACEHOLDER" },
{ "path": "/subdirectory/", "mustContain": "yoda:RANDOMNESS_PLACEHOLDER" }
]
}

View File

@@ -0,0 +1,5 @@
# Testing Markdown
yoda:RANDOMNESS_PLACEHOLDER
[Wow a link!](https://zeit.co)

View File

@@ -0,0 +1,33 @@
/* global beforeAll, expect, it, jest */
const fs = require('fs');
const path = require('path');
const {
packAndDeploy,
testDeployment,
} = require('../../../test/lib/deployment/test-deployment.js');
jest.setTimeout(2 * 60 * 1000);
const buildUtilsUrl = '@canary';
let builderUrl;
beforeAll(async () => {
const builderPath = path.resolve(__dirname, '..');
builderUrl = await packAndDeploy(builderPath);
console.log('builderUrl', builderUrl);
});
const fixturesPath = path.resolve(__dirname, 'fixtures');
// eslint-disable-next-line no-restricted-syntax
for (const fixture of fs.readdirSync(fixturesPath)) {
// eslint-disable-next-line no-loop-func
it(`should build ${fixture}`, async () => {
await expect(
testDeployment(
{ builderUrl, buildUtilsUrl },
path.join(fixturesPath, fixture),
),
).resolves.toBeDefined();
});
}

View File

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

View File

@@ -14,7 +14,7 @@ exports.build = async ({ files, entrypoint, workPath }) => {
console.log('downloading user files...');
const downloadedFiles = await download(files, workPath);
console.log('writing package.json...');
const packageJson = { dependencies: { 'mdx-deck': '1.7.7' } };
const packageJson = { dependencies: { 'mdx-deck': '1.7.15' } };
const packageJsonPath = path.join(workPath, 'package.json');
await writeFile(packageJsonPath, JSON.stringify(packageJson));
console.log('running npm install...');
@@ -47,7 +47,7 @@ exports.build = async ({ files, entrypoint, workPath }) => {
exports.prepareCache = async ({ cachePath }) => {
console.log('writing package.json...');
const packageJson = { dependencies: { 'mdx-deck': '1.7.7' } };
const packageJson = { dependencies: { 'mdx-deck': '1.7.15' } };
const packageJsonPath = path.join(cachePath, 'package.json');
await writeFile(packageJsonPath, JSON.stringify(packageJson));
console.log('running npm install...');

View File

@@ -1,7 +1,10 @@
{
"name": "@now/mdx-deck",
"version": "0.4.16",
"version": "0.4.17",
"peerDependencies": {
"@now/build-utils": ">=0.0.1"
},
"scripts": {
"test": "jest"
}
}

View File

@@ -0,0 +1,5 @@
# Testing Markdown
---
cow:RANDOMNESS_PLACEHOLDER
---
[Wow a link!](https://zeit.co)

View File

@@ -0,0 +1,11 @@
{
"version": 2,
"builds": [
{ "src": "index.mdx", "use": "@now/mdx-deck" },
{ "src": "subdirectory/index.mdx", "use": "@now/mdx-deck" }
],
"probes": [
{ "path": "/", "mustContain": "cow:RANDOMNESS_PLACEHOLDER" },
{ "path": "/subdirectory/", "mustContain": "yoda:RANDOMNESS_PLACEHOLDER" }
]
}

View File

@@ -0,0 +1,5 @@
# Testing Markdown
---
yoda:RANDOMNESS_PLACEHOLDER
---
[Wow a link!](https://zeit.co)

View File

@@ -0,0 +1,33 @@
/* global beforeAll, expect, it, jest */
const fs = require('fs');
const path = require('path');
const {
packAndDeploy,
testDeployment,
} = require('../../../test/lib/deployment/test-deployment.js');
jest.setTimeout(2 * 60 * 1000);
const buildUtilsUrl = '@canary';
let builderUrl;
beforeAll(async () => {
const builderPath = path.resolve(__dirname, '..');
builderUrl = await packAndDeploy(builderPath);
console.log('builderUrl', builderUrl);
});
const fixturesPath = path.resolve(__dirname, 'fixtures');
// eslint-disable-next-line no-restricted-syntax
for (const fixture of fs.readdirSync(fixturesPath)) {
// eslint-disable-next-line no-loop-func
it(`should build ${fixture}`, async () => {
await expect(
testDeployment(
{ builderUrl, buildUtilsUrl },
path.join(fixturesPath, fixture),
),
).resolves.toBeDefined();
});
}

View File

@@ -17,6 +17,7 @@ const {
excludeLockFiles,
normalizePackageJson,
excludeStaticDirectory,
onlyStaticDirectory,
} = require('./utils');
/** @typedef { import('@now/build-utils/file-ref').Files } Files */
@@ -91,7 +92,7 @@ exports.build = async ({ files, workPath, entrypoint }) => {
const filesWithoutStaticDirectory = excludeStaticDirectory(
filesWithoutLockfiles,
);
let downloadedFiles = await download(filesWithoutStaticDirectory, workPath);
const downloadedFiles = await download(filesWithoutStaticDirectory, workPath);
console.log('normalizing package.json');
const packageJson = normalizePackageJson(
@@ -105,8 +106,6 @@ exports.build = async ({ files, workPath, entrypoint }) => {
await writeNpmRc(workPath, process.env.NPM_AUTH_TOKEN);
}
downloadedFiles = await glob('**', workPath);
console.log('running npm install...');
await runNpmInstall(workPath, ['--prefer-offline']);
console.log('running user script...');
@@ -117,7 +116,7 @@ exports.build = async ({ files, workPath, entrypoint }) => {
await unlink(path.join(workPath, '.npmrc'));
}
downloadedFiles = await glob('**', workPath);
const filesAfterBuild = await glob('**', workPath);
console.log('preparing lambda files...');
let buildId;
@@ -144,8 +143,8 @@ exports.build = async ({ files, workPath, entrypoint }) => {
...dotNextServerRootFiles,
...launcherFiles,
};
if (downloadedFiles['next.config.js']) {
nextFiles['next.config.js'] = downloadedFiles['next.config.js'];
if (filesAfterBuild['next.config.js']) {
nextFiles['next.config.js'] = filesAfterBuild['next.config.js'];
}
const pages = await glob(
'**/*.js',
@@ -169,16 +168,16 @@ exports.build = async ({ files, workPath, entrypoint }) => {
);
const pageFiles = {
[`.next/server/static/${buildId}/pages/_document.js`]: downloadedFiles[
[`.next/server/static/${buildId}/pages/_document.js`]: filesAfterBuild[
`.next/server/static/${buildId}/pages/_document.js`
],
[`.next/server/static/${buildId}/pages/_app.js`]: downloadedFiles[
[`.next/server/static/${buildId}/pages/_app.js`]: filesAfterBuild[
`.next/server/static/${buildId}/pages/_app.js`
],
[`.next/server/static/${buildId}/pages/_error.js`]: downloadedFiles[
[`.next/server/static/${buildId}/pages/_error.js`]: filesAfterBuild[
`.next/server/static/${buildId}/pages/_error.js`
],
[`.next/server/static/${buildId}/pages/${page}`]: downloadedFiles[
[`.next/server/static/${buildId}/pages/${page}`]: filesAfterBuild[
`.next/server/static/${buildId}/pages/${page}`
],
};
@@ -209,12 +208,36 @@ exports.build = async ({ files, workPath, entrypoint }) => {
{},
);
return { ...lambdas, ...staticFiles };
const nextStaticDirectory = onlyStaticDirectory(filesWithoutLockfiles);
const staticDirectoryFiles = Object.keys(nextStaticDirectory).reduce(
(mappedFiles, file) => ({
...mappedFiles,
[path.join(entryDirectory, file)]: nextStaticDirectory[file],
}),
{},
);
return { ...lambdas, ...staticFiles, ...staticDirectoryFiles };
};
exports.prepareCache = async ({ files, cachePath, workPath }) => {
exports.prepareCache = async ({
files, entrypoint, cachePath, workPath,
}) => {
console.log('downloading user files...');
await download(files, cachePath);
const entryDirectory = path.dirname(entrypoint);
const filesOnlyEntryDirectory = includeOnlyEntryDirectory(
files,
entryDirectory,
);
const filesWithEntryDirectoryRoot = moveEntryDirectoryToRoot(
filesOnlyEntryDirectory,
entryDirectory,
);
const filesWithoutLockfiles = excludeLockFiles(filesWithEntryDirectoryRoot);
const filesWithoutStaticDirectory = excludeStaticDirectory(
filesWithoutLockfiles,
);
await download(filesWithoutStaticDirectory, workPath);
await download(await glob('.next/**', workPath), cachePath);
await download(await glob('node_modules/**', workPath), cachePath);

View File

@@ -1,6 +1,6 @@
{
"name": "@now/next",
"version": "0.0.80",
"version": "0.0.81",
"dependencies": {
"@now/node-bridge": "0.1.4",
"execa": "^1.0.0",

View File

@@ -111,6 +111,19 @@ function excludeStaticDirectory(files) {
return excludeFiles(files, matcher);
}
/**
* Exclude the static directory from files
* @param {Files} files
* @returns {Files}
*/
function onlyStaticDirectory(files) {
function matcher(filePath) {
return !filePath.startsWith('static');
}
return excludeFiles(files, matcher);
}
/**
* Enforce specific package.json configuration for smallest possible lambda
* @param {{dependencies?: any, devDependencies?: any, scripts?: any}} defaultPackageJson
@@ -164,4 +177,5 @@ module.exports = {
excludeLockFiles,
normalizePackageJson,
excludeStaticDirectory,
onlyStaticDirectory,
};

View File

@@ -47,7 +47,7 @@ async function downloadInstallAndBundle(
'package.json': new FileBlob({
data: JSON.stringify({
dependencies: {
'@zeit/ncc': '0.1.12',
'@zeit/ncc': '0.4.1',
},
}),
}),

View File

@@ -1,6 +1,6 @@
{
"name": "@now/node-server",
"version": "0.4.24",
"version": "0.4.25",
"dependencies": {
"@now/node-bridge": "^0.1.9",
"fs-extra": "7.0.1"

View File

@@ -6,6 +6,6 @@
],
"probes": [
{ "path": "/", "mustContain": "cow:RANDOMNESS_PLACEHOLDER" },
{ "path": "/subdirectory", "mustContain": "yoda:RANDOMNESS_PLACEHOLDER" }
{ "path": "/subdirectory/", "mustContain": "yoda:RANDOMNESS_PLACEHOLDER" }
]
}

View File

@@ -0,0 +1,15 @@
const fs = require('fs');
const http = require('http');
const path = require('path');
const server = http.createServer((req, resp) => {
const asset1 = fs.readFileSync(
path.join(__dirname, 'subdirectory1/asset.txt'),
);
const asset2 = fs.readFileSync(
path.join(__dirname, 'subdirectory2/asset.txt'),
);
resp.end(`${asset1},${asset2}`);
});
server.listen();

View File

@@ -0,0 +1,9 @@
{
"version": 2,
"builds": [
{ "src": "index.js", "use": "@now/node-server" }
],
"probes": [
{ "path": "/", "mustContain": "asset1:RANDOMNESS_PLACEHOLDER,asset2:RANDOMNESS_PLACEHOLDER" }
]
}

View File

@@ -0,0 +1 @@
asset1:RANDOMNESS_PLACEHOLDER

View File

@@ -0,0 +1 @@
asset2:RANDOMNESS_PLACEHOLDER

File diff suppressed because it is too large Load Diff

View File

@@ -8,6 +8,7 @@ const {
} = require('../../../test/lib/deployment/test-deployment.js');
jest.setTimeout(2 * 60 * 1000);
const buildUtilsUrl = '@canary';
let builderUrl;
beforeAll(async () => {
@@ -23,7 +24,10 @@ for (const fixture of fs.readdirSync(fixturesPath)) {
// eslint-disable-next-line no-loop-func
it(`should build ${fixture}`, async () => {
await expect(
testDeployment({ builderUrl }, path.join(fixturesPath, fixture)),
).resolves.toBe(undefined);
testDeployment(
{ builderUrl, buildUtilsUrl },
path.join(fixturesPath, fixture),
),
).resolves.toBeDefined();
});
}

View File

@@ -45,7 +45,7 @@ async function downloadInstallAndBundle(
'package.json': new FileBlob({
data: JSON.stringify({
dependencies: {
'@zeit/ncc': '0.1.12',
'@zeit/ncc': '0.4.1',
},
}),
}),
@@ -109,6 +109,7 @@ exports.build = async ({ files, entrypoint, workPath }) => {
[
'process.chdir("./user");',
`listener = require("./${path.join('user', entrypoint)}");`,
'if (listener.default) listener = listener.default;',
].join(' '),
);

View File

@@ -1,6 +1,6 @@
{
"name": "@now/node",
"version": "0.4.26",
"version": "0.4.27",
"dependencies": {
"@now/node-bridge": "^0.1.9",
"fs-extra": "7.0.1"

View File

@@ -6,6 +6,6 @@
],
"probes": [
{ "path": "/", "mustContain": "cow:RANDOMNESS_PLACEHOLDER" },
{ "path": "/subdirectory", "mustContain": "yoda:RANDOMNESS_PLACEHOLDER" }
{ "path": "/subdirectory/", "mustContain": "yoda:RANDOMNESS_PLACEHOLDER" }
]
}

View File

@@ -0,0 +1,12 @@
const fs = require('fs');
const path = require('path');
module.exports = (req, resp) => {
const asset1 = fs.readFileSync(
path.join(__dirname, 'subdirectory1/asset.txt'),
);
const asset2 = fs.readFileSync(
path.join(__dirname, 'subdirectory2/asset.txt'),
);
resp.end(`${asset1},${asset2}`);
};

View File

@@ -0,0 +1,9 @@
{
"version": 2,
"builds": [
{ "src": "index.js", "use": "@now/node" }
],
"probes": [
{ "path": "/", "mustContain": "asset1:RANDOMNESS_PLACEHOLDER,asset2:RANDOMNESS_PLACEHOLDER" }
]
}

View File

@@ -0,0 +1 @@
asset1:RANDOMNESS_PLACEHOLDER

View File

@@ -0,0 +1 @@
asset2:RANDOMNESS_PLACEHOLDER

View File

@@ -8,6 +8,7 @@ const {
} = require('../../../test/lib/deployment/test-deployment.js');
jest.setTimeout(2 * 60 * 1000);
const buildUtilsUrl = '@canary';
let builderUrl;
beforeAll(async () => {
@@ -23,7 +24,10 @@ for (const fixture of fs.readdirSync(fixturesPath)) {
// eslint-disable-next-line no-loop-func
it(`should build ${fixture}`, async () => {
await expect(
testDeployment({ builderUrl }, path.join(fixturesPath, fixture)),
).resolves.toBe(undefined);
testDeployment(
{ builderUrl, buildUtilsUrl },
path.join(fixturesPath, fixture),
),
).resolves.toBeDefined();
});
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -12,6 +12,7 @@ RUN go get -v github.com/aws/aws-lambda-go/events
RUN go get -v github.com/deuill/go-php
WORKDIR /root/go/app
COPY ./utils/bridge.go /root/go/app/utils/bridge.go
COPY ./launcher.go /root/go/app/launcher.go
COPY ./php.ini /root/go/app/php.ini
COPY ./test.go /root/go/app/test.go

View File

@@ -1,5 +1,7 @@
rm -rf ../dist
mkdir -p ../dist/modules
mkdir ./utils
cp ../../../utils/go/bridge/bridge.go ./utils/bridge.go
docker rmi go-php-builder --force
docker build . -t go-php-builder
docker run go-php-builder
@@ -10,3 +12,4 @@ docker run go-php-builder /bin/cat /usr/lib64/php/modules/curl.so > ../dist/modu
docker run go-php-builder /bin/cat /usr/lib64/php/modules/json.so > ../dist/modules/json.so
docker run go-php-builder /bin/cat /usr/lib64/php/modules/mbstring.so > ../dist/modules/mbstring.so
chmod +x ../dist/launcher
rm -rf ./utils

View File

@@ -1,56 +1,26 @@
package main
import (
now "./utils"
"bytes"
"context"
"encoding/base64"
"encoding/json"
php "github.com/deuill/go-php"
"net/http"
"os"
"path"
"path/filepath"
"strings"
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
php "github.com/deuill/go-php"
)
type Request struct {
Host string `json:"host"`
Path string `json:"path"`
Method string `json:"method"`
Headers map[string]string `json:"headers"`
Encoding string `json:"encoding,omitempty"`
Body string `json:"body"`
type PhpHandler struct {
http.Handler
ScriptFull string
}
var phpScript = ""
var phpScriptFull = ""
func handler(ctx context.Context, event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
func (h *PhpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
engine, _ := php.New()
context, _ := engine.NewContext()
var req Request
json.Unmarshal([]byte(event.Body), &req)
var body string
if req.Encoding == "base64" {
decoded, _ := base64.StdEncoding.DecodeString(req.Body)
body = string(decoded)
} else {
body = string(req.Body)
}
var bodyReader = strings.NewReader(body)
var httpReq, _ = http.NewRequest(req.Method, req.Path, bodyReader)
for k, v := range req.Headers {
httpReq.Header.Add(k, v)
}
var query = httpReq.URL.Query()
var query = r.URL.Query()
getMap := make(map[string]string)
for k, v := range query {
for _, s := range v {
@@ -59,9 +29,9 @@ func handler(ctx context.Context, event events.APIGatewayProxyRequest) (events.A
}
context.Bind("_GET", getMap)
httpReq.ParseForm()
r.ParseForm()
postMap := make(map[string]string)
for k, v := range httpReq.PostForm {
for k, v := range r.PostForm {
for _, s := range v {
postMap[k] = s
}
@@ -75,29 +45,36 @@ func handler(ctx context.Context, event events.APIGatewayProxyRequest) (events.A
}
context.Bind("_ENV", envMap)
context.Eval("$_SERVER[\"SERVER_NAME\"]=\"" + req.Host + "\";")
context.Eval("$_SERVER[\"SERVER_NAME\"]=\"" + r.Host + "\";")
context.Eval("$_SERVER[\"SERVER_PORT\"]=\"443\";")
context.Eval("$_SERVER[\"HTTPS\"]=\"on\";")
context.Eval("http_response_code(200);")
var stdout bytes.Buffer
context.Output = &stdout
context.Exec(phpScriptFull)
context.Exec(h.ScriptFull)
headers := make(map[string]string)
headers["content-type"] = "text/html"
statusCodeVal, _ := context.Eval("return http_response_code();")
w.WriteHeader(int(statusCodeVal.Int()))
headers := w.Header()
headers.Add("content-type", "text/html")
for k, v := range context.Header {
for _, s := range v {
headers[k] = s
headers.Add(k, s)
}
}
w.Write(stdout.Bytes())
engine.Destroy()
return events.APIGatewayProxyResponse{StatusCode: 200, Headers: headers, Body: stdout.String()}, nil
}
func main() {
ex, _ := os.Executable()
phpScript = os.Getenv("NOW_PHP_SCRIPT")
phpScriptFull = path.Join(filepath.Dir(ex), phpScript)
lambda.Start(handler)
handler := &PhpHandler{
nil,
path.Join(filepath.Dir(ex), os.Getenv("NOW_PHP_SCRIPT")),
}
now.Start(handler)
}

View File

@@ -1,6 +1,6 @@
{
"name": "@now/php",
"version": "0.4.11",
"version": "0.4.12",
"peerDependencies": {
"@now/build-utils": ">=0.0.1"
},

View File

@@ -6,6 +6,6 @@
],
"probes": [
{ "path": "/", "mustContain": "cow:RANDOMNESS_PLACEHOLDER" },
{ "path": "/subdirectory", "mustContain": "yoda:RANDOMNESS_PLACEHOLDER" }
{ "path": "/subdirectory/", "mustContain": "yoda:RANDOMNESS_PLACEHOLDER" }
]
}

View File

@@ -8,6 +8,7 @@ const {
} = require('../../../test/lib/deployment/test-deployment.js');
jest.setTimeout(2 * 60 * 1000);
const buildUtilsUrl = '@canary';
let builderUrl;
beforeAll(async () => {
@@ -23,7 +24,10 @@ for (const fixture of fs.readdirSync(fixturesPath)) {
// eslint-disable-next-line no-loop-func
it(`should build ${fixture}`, async () => {
await expect(
testDeployment({ builderUrl }, path.join(fixturesPath, fixture)),
).resolves.toBe(undefined);
testDeployment(
{ builderUrl, buildUtilsUrl },
path.join(fixturesPath, fixture),
),
).resolves.toBeDefined();
});
}

View File

@@ -1,6 +1,6 @@
{
"name": "@now/static-build",
"version": "0.4.15",
"version": "0.4.16",
"peerDependencies": {
"@now/build-utils": ">=0.0.1"
},

View File

@@ -6,6 +6,6 @@
],
"probes": [
{ "path": "/", "mustContain": "cow:RANDOMNESS_PLACEHOLDER" },
{ "path": "/subdirectory", "mustContain": "yoda:RANDOMNESS_PLACEHOLDER" }
{ "path": "/subdirectory/", "mustContain": "yoda:RANDOMNESS_PLACEHOLDER" }
]
}

View File

@@ -6,6 +6,6 @@
],
"probes": [
{ "path": "/", "mustContain": "cow:RANDOMNESS_PLACEHOLDER" },
{ "path": "/subdirectory", "mustContain": "yoda:RANDOMNESS_PLACEHOLDER" }
{ "path": "/subdirectory/", "mustContain": "yoda:RANDOMNESS_PLACEHOLDER" }
]
}

View File

@@ -8,6 +8,7 @@ const {
} = require('../../../test/lib/deployment/test-deployment.js');
jest.setTimeout(2 * 60 * 1000);
const buildUtilsUrl = '@canary';
let builderUrl;
beforeAll(async () => {
@@ -23,7 +24,10 @@ for (const fixture of fs.readdirSync(fixturesPath)) {
// eslint-disable-next-line no-loop-func
it(`should build ${fixture}`, async () => {
await expect(
testDeployment({ builderUrl }, path.join(fixturesPath, fixture)),
).resolves.toBe(undefined);
testDeployment(
{ builderUrl, buildUtilsUrl },
path.join(fixturesPath, fixture),
),
).resolves.toBeDefined();
});
}

View File

@@ -41,3 +41,14 @@ it('Should throw when package.json or next.config.js is not the "src"', async ()
expect(err.message).toMatch(/package\.json/);
}
});
it(
'Should build the static-files test',
async () => {
const { buildResult } = await runBuildLambda(
path.join(__dirname, 'static-files'),
);
expect(buildResult['static/test.txt']).toBeDefined();
},
FOUR_MINUTES,
);

View File

@@ -0,0 +1 @@
module.exports = {};

View File

@@ -0,0 +1,6 @@
{
"version": 2,
"builds": [
{"src": "next.config.js", "use": "@now/next"}
]
}

View File

@@ -0,0 +1 @@
export default () => 'Index page';

View File

@@ -0,0 +1 @@
hello world

View File

@@ -19,6 +19,7 @@
"no-restricted-syntax": "off",
"no-return-await": "off",
"no-use-before-define": "off",
"prefer-destructuring": "off",
"space-before-function-paren": [
"error",
"always"

View File

@@ -1,12 +1,9 @@
const assert = require('assert');
const { createHash } = require('crypto');
const fetch = require('node-fetch');
const fs = require('fs-extra');
const { homedir } = require('os');
const path = require('path');
const API_URL = 'https://api.zeit.co';
async function nowDeploy (bodies, randomness) {
const files = Object.keys(bodies)
.filter((n) => n !== 'now.json')
@@ -55,7 +52,7 @@ async function nowDeploy (bodies, randomness) {
await new Promise((r) => setTimeout(r, 1000));
}
return deploymentUrl;
return { deploymentId, deploymentUrl };
}
function digestOfFile (body) {
@@ -74,7 +71,7 @@ async function filePost (body, digest) {
'x-now-size': body.length
};
const resp = await fetchWithAuth(`${API_URL}/v2/now/files`, {
const resp = await fetchWithAuth('/v2/now/files', {
method: 'POST',
headers,
body
@@ -84,7 +81,7 @@ async function filePost (body, digest) {
}
async function deploymentPost (payload) {
const resp = await fetchWithAuth(`${API_URL}/v6/now/deployments?forceNew=1`, {
const resp = await fetchWithAuth('/v6/now/deployments?forceNew=1', {
method: 'POST',
body: JSON.stringify(payload)
});
@@ -94,28 +91,30 @@ async function deploymentPost (payload) {
}
async function deploymentGet (deploymentId) {
const resp = await fetchWithAuth(
`${API_URL}/v3/now/deployments/${deploymentId}`
);
const resp = await fetchWithAuth(`/v3/now/deployments/${deploymentId}`);
return await resp.json();
}
async function fetchWithAuth (url, opts = {}) {
const apiHost = process.env.API_HOST || 'api.zeit.co';
const urlWithHost = `https://${apiHost}${url}`;
if (!opts.headers) opts.headers = {};
const authJsonPath = path.join(homedir(), '.now/auth.json');
if (!(await fs.exists(authJsonPath))) {
await fs.mkdirp(path.dirname(authJsonPath));
await fs.writeFile(
authJsonPath,
JSON.stringify({
token: process.env.NOW_AUTH_TOKEN
})
);
let token;
if (process.env.NOW_AUTH_TOKENS) {
const tokens = process.env.NOW_AUTH_TOKENS.split(',');
if (process.env.CIRCLE_BUILD_NUM) {
token = tokens[Number(process.env.CIRCLE_BUILD_NUM) % tokens.length];
} else {
token = tokens[Math.floor(Math.random() * tokens.length)];
}
} else {
const authJsonPath = path.join(homedir(), '.now/auth.json');
token = require(authJsonPath).token;
}
const { token } = require(authJsonPath);
opts.headers.Authorization = `Bearer ${token}`;
return await fetchApiWithChecks(url, opts);
return await fetchApiWithChecks(urlWithHost, opts);
}
async function fetchApiWithChecks (url, opts = {}) {

View File

@@ -1,4 +1,5 @@
const assert = require('assert');
const bufferReplace = require('buffer-replace');
const fetch = require('node-fetch');
const fs = require('fs-extra');
const glob = require('util').promisify(require('glob'));
@@ -19,6 +20,8 @@ async function packAndDeploy (builderPath) {
return url;
}
const RANDOMNESS_PLACEHOLDER_STRING = 'RANDOMNESS_PLACEHOLDER';
async function testDeployment ({ builderUrl, buildUtilsUrl }, fixturePath) {
console.log('testDeployment', fixturePath);
const globResult = await glob(`${fixturePath}/**`, { nodir: true });
@@ -28,33 +31,42 @@ async function testDeployment ({ builderUrl, buildUtilsUrl }, fixturePath) {
return b;
}, {});
const randomness = Math.floor(Math.random() * 0x7fffffff).toString(16);
const randomness = Math.floor(Math.random() * 0x7fffffff)
.toString(16)
.repeat(6)
.slice(0, RANDOMNESS_PLACEHOLDER_STRING.length);
for (const file of Object.keys(bodies)) {
if ([ '.js', '.json', '.php', '.sh' ].includes(path.extname(file))) {
bodies[file] = Buffer.from(
bodies[file].toString().replace(/RANDOMNESS_PLACEHOLDER/g, randomness)
);
}
bodies[file] = bufferReplace(
bodies[file],
RANDOMNESS_PLACEHOLDER_STRING,
randomness
);
}
const nowJson = JSON.parse(bodies['now.json']);
for (const build of nowJson.builds) {
if (builderUrl) {
build.use = `https://${builderUrl}`;
if (!buildUtilsUrl) {
build.config = build.config || {};
build.config.useBuildUtils = '@now/build-utils@canary';
if (builderUrl === '@canary') {
build.use = `${build.use}@canary`;
} else {
build.use = `https://${builderUrl}`;
}
}
if (buildUtilsUrl) {
if (!builderUrl) build.use = `${build.use}@canary`;
build.config = build.config || {};
build.config.useBuildUtils = `https://${buildUtilsUrl}`;
const { config } = build;
if (buildUtilsUrl === '@canary') {
config.useBuildUtils = config.useBuildUtils || '@now/build-utils';
config.useBuildUtils = `${config.useBuildUtils}@canary`;
} else {
config.useBuildUtils = `https://${buildUtilsUrl}`;
}
}
}
bodies['now.json'] = Buffer.from(JSON.stringify(nowJson));
const deploymentUrl = await nowDeploy(bodies, randomness);
const { deploymentId, deploymentUrl } = await nowDeploy(bodies, randomness);
console.log('deploymentUrl', deploymentUrl);
for (const probe of nowJson.probes) {
@@ -78,6 +90,8 @@ async function testDeployment ({ builderUrl, buildUtilsUrl }, fixturePath) {
assert(false, 'probe must have a test condition');
}
}
return { deploymentId, deploymentUrl };
}
async function nowDeployIndexTgz (file) {
@@ -86,7 +100,7 @@ async function nowDeployIndexTgz (file) {
'now.json': Buffer.from(JSON.stringify({ version: 2 }))
};
return await nowDeploy(bodies);
return (await nowDeploy(bodies)).deploymentUrl;
}
async function fetchDeploymentUrl (url, opts) {

View File

@@ -6,6 +6,7 @@ const {
excludeLockFiles,
normalizePackageJson,
excludeStaticDirectory,
onlyStaticDirectory,
} = require('@now/next/utils');
const FileRef = require('@now/build-utils/file-ref');
@@ -170,6 +171,38 @@ describe('excludeStaticDirectory', () => {
});
});
describe('onlyStaticDirectory', () => {
it('should keep only /static directory files', () => {
const files = {
'frontend/pages/index.js': new FileRef({ digest: 'index' }),
'package.json': new FileRef({ digest: 'package' }),
'yarn.lock': new FileRef({ digest: 'yarn-lock' }),
'package-lock.json': new FileRef({ digest: 'package-lock' }),
'static/image.png': new FileRef({ digest: 'image' }),
};
const result = onlyStaticDirectory(files);
expect(result['frontend/pages/index.js']).toBeUndefined();
expect(result['yarn.lock']).toBeUndefined();
expect(result['package-lock.json']).toBeUndefined();
expect(result['static/image.png']).toBeDefined();
});
it('should keep nested /static directory files', () => {
const files = {
'frontend/pages/index.js': new FileRef({ digest: 'index' }),
'package.json': new FileRef({ digest: 'package' }),
'yarn.lock': new FileRef({ digest: 'yarn-lock' }),
'package-lock.json': new FileRef({ digest: 'package-lock' }),
'static/images/png/image.png': new FileRef({ digest: 'image' }),
};
const result = onlyStaticDirectory(files);
expect(result['frontend/pages/index.js']).toBeUndefined();
expect(result['yarn.lock']).toBeUndefined();
expect(result['package-lock.json']).toBeUndefined();
expect(result['static/images/png/image.png']).toBeDefined();
});
});
describe('normalizePackageJson', () => {
it('should work without a package.json being supplied', () => {
const result = normalizePackageJson();

130
utils/go/bridge/bridge.go Normal file
View File

@@ -0,0 +1,130 @@
package bridge
import (
"bytes"
"encoding/base64"
"encoding/json"
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
"net/http"
"strconv"
"strings"
)
type Request struct {
Host string `json:"host"`
Path string `json:"path"`
Method string `json:"method"`
Headers map[string]string `json:"headers"`
Encoding string `json:"encoding,omitempty"`
Body string `json:"body"`
}
type Response struct {
StatusCode int `json:"statusCode"`
Headers map[string]string `json:"headers"`
Encoding string `json:"encoding,omitemtpy"`
Body string `json:"body"`
}
type ResponseWriter struct {
http.ResponseWriter
statusCode int
headers http.Header
body *bytes.Buffer
}
func (w *ResponseWriter) Header() http.Header {
return w.headers
}
func (w *ResponseWriter) Write(p []byte) (n int, err error) {
n, err = w.body.Write(p)
return
}
func (w *ResponseWriter) WriteHeader(statusCode int) {
w.statusCode = statusCode
}
var userHandler http.Handler
func Serve(handler http.Handler, req *Request) (res Response, err error) {
var body []byte
if req.Encoding == "base64" {
body, err = base64.StdEncoding.DecodeString(req.Body)
if err != nil {
return
}
} else {
body = []byte(req.Body)
}
r, err := http.NewRequest(req.Method, req.Path, bytes.NewReader(body))
if err != nil {
return
}
for k, v := range req.Headers {
r.Header.Add(k, v)
switch strings.ToLower(k) {
case "host":
r.Host = v
case "content-length":
contentLength, _ := strconv.ParseInt(v, 10, 64)
r.ContentLength = contentLength
case "x-forwarded-for":
case "x-real-ip":
r.RemoteAddr = v
}
if strings.ToLower(k) == "host" {
// we need to set `Host` in the request
// because Go likes to ignore the `Host` header
// see https://github.com/golang/go/issues/7682
r.Host = v
}
}
var bodyBuf bytes.Buffer
w := &ResponseWriter{
nil,
http.StatusOK,
make(http.Header),
&bodyBuf,
}
handler.ServeHTTP(w, r)
defer r.Body.Close()
headers := make(map[string]string)
for k, v := range w.headers {
for _, s := range v {
headers[k] = s
}
}
res = Response{
StatusCode: w.statusCode,
Headers: headers,
Encoding: "base64",
Body: base64.StdEncoding.EncodeToString(bodyBuf.Bytes()),
}
return
}
// Maps the `APIGatewayProxyRequest` to a `Request` instance and invokes `Serve()`
func handler(event events.APIGatewayProxyRequest) (res Response, err error) {
var req Request
err = json.Unmarshal([]byte(event.Body), &req)
if err != nil {
return
}
res, err = Serve(userHandler, &req)
return
}
// Starts the Lambda
func Start(h http.Handler) {
userHandler = h
lambda.Start(handler)
}

View File

@@ -0,0 +1,43 @@
package bridge
import (
"encoding/base64"
"fmt"
"net/http"
"testing"
)
type HttpHandler struct {
http.Handler
t *testing.T
}
func (h *HttpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Header().Add("X-Foo", "bar")
w.WriteHeader(404)
w.Write([]byte("test"))
}
func TestServe(t *testing.T) {
h := &HttpHandler{nil, t}
req := &Request{
"test.com",
"/path?foo=bar",
"POST",
map[string]string{"Content-Length": "1", "X-Foo": "bar"},
"",
"a",
}
res, err := Serve(h, req)
if err != nil {
t.Fail()
}
if res.StatusCode != 404 {
t.Fail()
}
fmt.Printf("status code: %d\n", res.StatusCode)
fmt.Printf("header: %v\n", res.Headers)
fmt.Printf("base64 body: %s\n", res.Body)
body, err := base64.StdEncoding.DecodeString(res.Body)
fmt.Printf("body: %s\n", body)
}

8609
yarn.lock Normal file

File diff suppressed because it is too large Load Diff