Skip to content

Commit 01ddb90

Browse files
committed
initial commit
0 parents  commit 01ddb90

File tree

14 files changed

+1678
-0
lines changed

14 files changed

+1678
-0
lines changed

.dockerignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
test

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
node_modules/
2+
build/
3+
4+
# tests generated files
5+
test/layer/
6+
test/lambda/lambda.zip

Cakefile

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
util = require 'util'
2+
_exec = util.promisify require('child_process').exec
3+
pkg = require './package.json'
4+
assert = require 'assert'
5+
6+
LAYER_NAME = 'coffeescript'
7+
NODE_VERSION = '8.10.0'
8+
COFFEESCRIPT_VERSION = pkg.dependencies.coffeescript.replace /[^\d\.]/, ''
9+
IMAGE_FILE = "node-provided-lambda-v#{NODE_VERSION.split('.')[0]}.x"
10+
OUTPUT_DIR = 'build'
11+
OUTPUT_FILE = "#{pkg.name}_#{pkg.version}"
12+
13+
exec = (command) ->
14+
try
15+
_exec command, maxBuffer: 1024 * 1000
16+
catch e
17+
throw new Error e
18+
19+
task 'build', 'builds and packages the runtime layer',
20+
(options) ->
21+
console.log """
22+
===========================================================
23+
runtime: #{OUTPUT_FILE}
24+
CoffeeScript: v#{COFFEESCRIPT_VERSION}
25+
NodeJs: v#{NODE_VERSION}
26+
===========================================================
27+
"""
28+
29+
await exec "
30+
docker build
31+
--build-arg NODE_VERSION=#{NODE_VERSION}
32+
-t #{IMAGE_FILE}
33+
.
34+
"
35+
console.log "building image... ok"
36+
37+
await exec "
38+
if [ ! -d build ]; then mkdir build; fi;
39+
docker run --rm #{IMAGE_FILE}
40+
cat /tmp/node-v#{NODE_VERSION}.zip > #{OUTPUT_DIR}/#{OUTPUT_FILE}.zip
41+
"
42+
console.log "packaging image... ok"
43+
44+
console.log """
45+
build completed successfully!
46+
output: #{OUTPUT_DIR}/#{OUTPUT_FILE}.zip
47+
"""
48+
49+
task 'publish', 'uploads packaged runtime layer to AWS',
50+
(option) ->
51+
await invoke 'build'
52+
await invoke 'test'
53+
54+
{ stdout, stderr } = await exec "
55+
aws lambda publish-layer-version
56+
--layer-name #{LAYER_NAME}
57+
--zip-file fileb://#{OUTPUT_DIR}/#{OUTPUT_FILE}.zip
58+
--description \"A CoffeeScript v#{COFFEESCRIPT_VERSION} custom runtime\"
59+
--license-info MIT
60+
--query Version
61+
--output text
62+
"
63+
console.log 'publishing layer... ok'
64+
65+
[ LAYER_VERSION ] = stdout.split(/\n/)
66+
await exec "
67+
aws lambda add-layer-version-permission
68+
--layer-name #{LAYER_NAME}
69+
--version-number #{LAYER_VERSION}
70+
--statement-id sid1
71+
--action lambda:GetLayerVersion
72+
--principal '*'
73+
"
74+
console.log 'publishing layer permissions... ok'
75+
76+
console.log """
77+
publish completed successfully!
78+
name: #{LAYER_NAME}
79+
version: #{LAYER_VERSION}
80+
"""
81+
82+
task 'test', 'runs test',
83+
(option) ->
84+
await exec "
85+
rm -rf test/layer;
86+
unzip #{OUTPUT_DIR}/#{OUTPUT_FILE}.zip -d test/layer;
87+
88+
cd test/lambda && npm ci && cd -;
89+
rm -f test/lambda/lambda.zip;
90+
zip -qyr test/lambda/lambda.zip test/lambda/index.coffee test/lambda/node_modules
91+
"
92+
console.log 'packaging test... ok'
93+
94+
console.log 'running test...'
95+
{ stdout, stderr } = await exec "
96+
docker run
97+
--rm
98+
-v $(PWD)/test/lambda:/var/task
99+
-v $(PWD)/test/layer:/opt
100+
lambci/lambda:provided
101+
index.handler
102+
"
103+
104+
[ result ] = stdout.split(/\n/)
105+
expected = '{"statusCode":200,"body":"{\\"message\\":\\"CoffeeScript Serverless v1.0! Your function executed successfully!\\",\\"input\\":{}}"}'
106+
107+
passed = result is expected
108+
console.log "running test... #{if passed then 'PASS' else 'FAIL'}"
109+
110+
if not passed
111+
console.log stderr
112+
process.exit 1
113+
114+
await exec "
115+
rm -rf test/layer;
116+
rm -f test/lambda/lambda.zip;
117+
"
118+
console.log 'cleaning up... ok'

Dockerfile

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
FROM lambci/lambda-base:build
2+
3+
COPY bootstrap bootstrap.coffee babel.config.js /opt/
4+
COPY node_modules /opt/node_modules/
5+
6+
ARG NODE_VERSION
7+
8+
RUN curl -sSL https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.xz | \
9+
tar -xJ -C /opt --strip-components 1 -- node-v${NODE_VERSION}-linux-x64/bin/node && \
10+
strip /opt/bin/node
11+
12+
RUN cd /opt && \
13+
zip -yr /tmp/node-v${NODE_VERSION}.zip ./*

LICENSE

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
MIT License
2+
3+
jimleuk/coffeeScript-lambda-runtime
4+
Copyright 2018 Jim Le
5+
6+
Permission is hereby granted, free of charge, to any person obtaining a copy
7+
of this software and associated documentation files (the "Software"), to deal
8+
in the Software without restriction, including without limitation the rights
9+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
copies of the Software, and to permit persons to whom the Software is
11+
furnished to do so, subject to the following conditions:
12+
13+
The above copyright notice and this permission notice shall be included in all
14+
copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
SOFTWARE.
23+
24+
---
25+
26+
MIT License
27+
28+
lambci/node-custom-lambda
29+
Copyright 2018 Michael Hart and LambCI contributors
30+
31+
Permission is hereby granted, free of charge, to any person obtaining a copy of
32+
this software and associated documentation files (the "Software"), to deal in
33+
the Software without restriction, including without limitation the rights to
34+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
35+
of the Software, and to permit persons to whom the Software is furnished to do
36+
so, subject to the following conditions:
37+
38+
The above copyright notice and this permission notice shall be included in all
39+
copies or substantial portions of the Software.
40+
41+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
42+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
43+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
44+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
45+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
46+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
47+
SOFTWARE.

README.md

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# CoffeeScript for AWS Lambda
2+
3+
A custom runtime for AWS Lambda to execute functions in CoffeeScript.
4+
5+
> **Note**: This repository is essentially a CoffeeScript port and fork of
6+
[Node-Custom-Lambda](https://github.com/lambci/node-custom-lambda). The only
7+
real difference is where `Node-Custom-Lambda` expects functions to return a
8+
promise, `coffeescript-lambda-runtime` opts to stay with the traditional
9+
callback method.
10+
11+
> **New to CoffeeScript?**
12+
> I recommend starting at https://coffeescript.org/
13+
14+
## How does it work?
15+
`CoffeeScript-lambda-runtime` works by taking care of the compiling and execution of CoffeeScript source code at time of request. This means end-users of the runtime are no longer required to compile their CoffeeScript code to javascript before uploading their functions to AWS Lambda.
16+
17+
Simply write your functions as you would for NodeJs and it should just work.
18+
```coffeescript
19+
# feel alive again!
20+
exports.handler = (event, context, callback) ->
21+
response =
22+
statusCode: 200,
23+
body:
24+
JSON.stringify
25+
message: 'CoffeeScript Serverless v1.0! Your function executed successfully!',
26+
input: event,
27+
28+
callback null, response
29+
```
30+
31+
## Version ARN
32+
33+
Project|CoffeeScript|NodeJS|ARN|
34+
|-|-|-|-|
35+
|v1.0.0|v2.3.2|v8.10.0|arn:aws:lambda:eu-west-2:321742921541:layer:coffeescript:3|
36+
37+
## Building the runtime layer
38+
39+
There are two ways to get started using the coffeescript runtime,
40+
1. build & upload your own copy (recommended)
41+
2. or use the ARN supplied above if you just want to give it a try
42+
43+
To start building your own, simply do the following once you have cloned the repo:
44+
45+
```
46+
# Make sure you have a copy of coffeescript/cake installed globally
47+
> npm install -g coffeescript
48+
49+
# Install required dependencies
50+
> npm install
51+
52+
# go into the project root and type `cake build`
53+
> cake build
54+
55+
===========================================================
56+
runtime: coffeescript-lambda-runtime_1.0.0
57+
CoffeeScript: v2.3.2
58+
NodeJs: v8.10.0
59+
===========================================================
60+
building image... ok
61+
packaging image... ok
62+
build completed successfully!
63+
output: build/coffeescript-lambda-runtime_1.0.0.zip
64+
```
65+
66+
## Publishing your runtime layer
67+
68+
Once you have your build (ie. `build/coffeescript-lambda-runtime_1.0.0.zip`),
69+
simply upload to your aws account to make it available to your functions.
70+
71+
Simpliest way to do this is to use the aws console via `aws lambda > layers > create layer`.
72+
73+
Alternatively you can use the `cake publish` task to achieve the same thing. Note that `cake publish` uses the aws-cli so requires that your aws credentials are available before the task is executed.
74+
75+
For more info, please read the [official documentation of AWS Lambda Layers](https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html).
76+
77+
## Credits
78+
79+
[Node-custom-lambda](https://github.com/lambci/node-custom-lambda) by [@mhart](https://github.com/mhart) - of which this project is "forked" from
80+
81+
## FAQs
82+
83+
> **Note**: This runtime does not include the AWS-SDK
84+
<details>
85+
<summary>Is this runtime practical?</summary>
86+
<p>
87+
If you're not too fussed about cold start times then yes! Just remember that everytime a container starts it has to compile coffeescript source code before it runs, which may or may not be slow depending on given compute power. A warm container will be quite fast.
88+
</p>
89+
</details>
90+
91+
<details>
92+
<summary>Can I write with all modern CoffeeScript syntax or only those compatible with NodeJs?</summary>
93+
<p>
94+
Under the hood, the runtime transpiles all CoffeeScript code using <a href="https://babeljs.io/" rel="noopener">babel</a> which is configured to best match the NodeJs environment. This will ensure that end-users can confidently use modern ES6+ syntax without worrying about polyfills.
95+
</p>
96+
</details>
97+

babel.config.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
module.exports = api => {
2+
api.cache(true)
3+
return {
4+
presets: [
5+
[
6+
'@babel/preset-env',
7+
{
8+
targets: {
9+
node: 'current'
10+
},
11+
}
12+
]
13+
]
14+
}
15+
}

bootstrap

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#!/opt/bin/node
2+
3+
const fs = require('fs')
4+
const CoffeeScript = require('coffeescript')
5+
6+
// We'll set this so all later .coffee files we require() are
7+
// compiled/transpiled automatically.
8+
CoffeeScript.register()
9+
10+
// bootstrap.coffee is our port of the node-custom-lambda (https://github.com/
11+
// lambci/node-custom-lambda) bootstrap.js. This file handles the lifecycle of
12+
// the lambda. Note: when a lambda runs this file, the cwd() is /var/task
13+
const bootstrap = '/opt/bootstrap.coffee'
14+
15+
// These are options we pass to the CoffeeScript compiler.
16+
const options = {
17+
bare: true,
18+
header: false,
19+
sourceMap: false,
20+
inlineMap: false,
21+
// Due to our execution happening inside /var/task but our babel.config.js
22+
// located in /opt, the transpilation step could never find our babel config
23+
// and would fail. We needed to pass the "root" option to babel to fix this
24+
// but the coffeescript cli --transpile option was unfortunately just a
25+
// boolean flag.
26+
// The workaround was to run our bootstrap.coffee using the coffescript
27+
// nodeJS api instead (hence this file is a nodeJS script) where it is
28+
// possible to pass the required 'root' option.
29+
transpile: { root: '/opt/' }
30+
}
31+
32+
fs.readFile(bootstrap, 'utf8', (err, data) => {
33+
if (err) {
34+
throw new Error(`Could not find ${bootstrap}: ${err.code} ${err.message}`);
35+
}
36+
// Unlike your everyday coffeescript workflow, we're not writing the
37+
// compiled output to disk but rather evaluated on the fly. The result is
38+
// that we can save a few millisecs by skipping the i/o.
39+
CoffeeScript.run(data, options);
40+
});

0 commit comments

Comments
 (0)