Compare commits

...

22 Commits

Author SHA1 Message Date
Steven
9767682006 Publish
- @now/build-utils@0.7.0
 - @now/go@0.5.2
 - @now/next@0.5.0
 - @now/node-bridge@1.2.1
 - @now/node-server@0.8.0
 - @now/node@0.10.0
 - @now/optipng@0.6.3
 - @now/python@0.2.8
 - @now/ruby@0.1.0
 - @now/rust@0.2.7
 - @now/static-build@0.6.0
2019-06-24 17:02:17 -04:00
Steven
3285b31721 Revert "[now-node] Fix express not overriding helpers properties" (#659)
This reverts commit 7a7d8a55fbbe4f8ee89ce50a3c62816fc95f28f5.
2019-06-24 17:00:18 -04:00
Steven
70353c7fc0 [now-build-utils] Fail if engines is invalid (#658)
* [now-build-utils] Fail if engines does not match

* Add test to throw when invalid semver

* Apply suggestions from leo

Co-Authored-By: Leo Lamprecht <mindrun@icloud.com>

* Fix ts error

* Print range so its clear what to add to engines
2019-06-24 15:50:48 -04:00
Nathan Cahill
f85cf99325 support local pipfile (#652) 2019-06-24 15:50:37 -04:00
Luc
8b14a46d04 Add git diff to publishing steps in readme (#646)
* add `git diff canary` to publishing to stable

* Improve readme
2019-06-24 15:50:32 -04:00
Steven
383cbfd82f [now-build-utils] Remove enginesMatch export (#650)
* [now-build-utils] Remove `enginesMatch` export

* Remove unused import
2019-06-24 15:50:23 -04:00
Steven
81e268a3c9 Add support for node 10 in all builders (#649)
* Add support for node 10 in all builders

* Fix meta undefined
2019-06-24 15:50:16 -04:00
Steven
ac8b33213b [now-build-utils] Enhance node version selection (#648)
* [now-build-utils] Enhance node version selection

* Fix test
2019-06-24 15:50:10 -04:00
piousdeer
de12e7b8c8 [now-static-build] Use cross-spawn and npm for now dev (#639)
* [now-static-build] Use `cross-spawn` and npm

* [now-static-build] Add `cross-spawn` dependency
2019-06-24 15:50:04 -04:00
ywg-jean
b9346603f0 Proposes a contributing guideline (#644)
* proposes a contributing guideline

the guideline outlines the process to open a pull request on this
repository and offers guidance on interpreting test errors.

* Wording improvement

As per @styfle suggestion

Co-Authored-By: Steven <steven@ceriously.com>

* Reword reference to code of conduct

As per @styfle's suggestion.

Co-Authored-By: Steven <steven@ceriously.com>

* fix local developement section title

Co-Authored-By: Steven <steven@ceriously.com>

* Improves project description wording

Co-Authored-By: Steven <steven@ceriously.com>

* Proper setup instructions

The ones provided initially were incomplete (might explain why I had some issues :) )

Co-Authored-By: Steven <steven@ceriously.com>

* updates description of tests

Co-Authored-By: Steven <steven@ceriously.com>

* Improve explanation of integration tests

Co-Authored-By: Steven <steven@ceriously.com>

* Update ncc bug qualification section

Co-Authored-By: Steven <steven@ceriously.com>

* fixes typo

Co-Authored-By: Steven <steven@ceriously.com>

* clarifies when and why to run ncc manually

Co-Authored-By: Steven <steven@ceriously.com>

* clarifies how to access to test deployements and associated logs

Co-Authored-By: Steven <steven@ceriously.com>
2019-06-24 15:50:00 -04:00
Luc
0b793dfc35 [now-node] Fix express not overriding helpers properties (#638)
* add breaking test

* fix test

* fix tests (bis)

* be explicit about @ts-ignore

* be more specific in test name
2019-06-24 15:49:52 -04:00
Steven
9dd672c383 [now-node] Change PATH to use node10 (#637)
* [now-node] Change PATH to use node10

* Fallback to node8
2019-06-24 15:49:46 -04:00
Steven
1b743aeea8 [now-ruby] Fix ruby publish step (#642) 2019-06-24 15:49:41 -04:00
Steven
d4af4b9f5c Run prettier (#635) 2019-06-24 15:49:35 -04:00
Steven
b734ca3e01 [now-build-utils] Add spawnOpts param to runNpmInstall() (#634) 2019-06-24 15:49:27 -04:00
Luc
f81d753104 [now-go] Fix failing build when go.mod exists in a subfolder (#633)
* add test

* fix test

* fix 14-go-mod-sub test fixtures

* add index.go to make fixtures correct
2019-06-24 15:49:21 -04:00
Steven
db31b9a207 [tests] Bump typescript to 3.5.2 (#631) 2019-06-24 15:49:07 -04:00
Nathan Cahill
b80b5182e6 [now-ruby] Add @now/ruby Builder (#454)
* add @now/ruby

* changes from feedback

* remove mm

* increase timeout

Co-Authored-By: Steven <steven@ceriously.com>

* address changes

* fix linting errors

* support arrays in includeFiles

* undo type change
2019-06-24 15:48:56 -04:00
Steven
268a7c2b81 [now-node] Enable node10.x runtime (#630) 2019-06-24 15:48:48 -04:00
Sophearak Tha
667a16c996 [now-go] add support for nested packages (#623)
* add support for nested packages

* properly clone array
2019-06-24 15:48:20 -04:00
Steven
7b851f81c0 Publish
- @now/build-utils@0.6.0
2019-06-17 14:25:30 -04:00
Steven
80fbbcd194 [now-build-utils] Add enginesMatch export (#618)
* Add engineSatisifies for node

* Rename to enginesMatch

* Add test for node10

* Minor refactor

* Add tests for engines, uses semver.intersect()

* Revert @now/node, new PR later
2019-06-17 14:22:22 -04:00
155 changed files with 3053 additions and 148 deletions

View File

@@ -12,3 +12,4 @@
/packages/now-optipng/dist/*
/packages/now-go/*
/packages/now-rust/dist/*
/packages/now-ruby/dist/*

85
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,85 @@
# Contributing
When contributing to this repository, please first discuss the change you wish to make via issue or [spectrum](https://spectrum.chat/zeit) with the owners of this repository before submitting a Pull Request.
Please read our [code of conduct](CODE_OF_CONDUCT.md) and follow it in all your interactions with the project.
## Local development
This project is configured in a monorepo pattern where one repo contains multiple npm packages. Dependencies are installed and managed with `yarn`, not `npm` CLI.
To get started, execute the following:
```
git clone https://github.com/zeit/now-builders
yarn install
yarn bootstrap
yarn build
yarn lint
yarn test
```
Make sure all the tests pass before making changes.
## Verifying your change
Once you are done with your changes (we even suggest doing it along the way ), make sure all the test still run by running
```
yarn build && yarn test
```
from the root of the project.
If any test fails, make sure to fix it along with your changes. See [Interpreting test errors](#Interpreting-test-errors) for more information about how the tests are executed, especially the integration tests.
## Pull Request Process
Once you are confident that your changes work properly, open a pull request on the main repository.
The pull request will be reviewed by the maintainers and the tests will be checked by our continuous integration platform.
## Interpreting test errors
There are 2 kinds of tests in this repository Unit tests and Integration tests.
Unit tests are run locally with `jest` and execute quickly because they are testing the smallest units of code.
### Integration tests
Integration tests create deployments to your ZEIT account using the `test` project name. After each test is deployed, the `probes` key is used to check if the response is the expected value. If the value doesn't match, you'll see a message explaining the difference. If the deployment failed to build, you'll see a more generic message like the following:
```
[Error: Fetched page https://test-8ashcdlew.now.sh/root.js does not contain hello Root!. Instead it contains An error occurred with this application.
NO_STATUS_CODE_FRO Response headers:
cache-control=s-maxage=0
connection=close
content-type=text/plain; charset=utf-8
date=Wed, 19 Jun 2019 18:01:37 GMT
server=now
strict-transport-security=max-age=63072000
transfer-encoding=chunked
x-now-id=iad1:hgtzj-1560967297876-44ae12559f95
x-now-trace=iad1]
```
In such cases you can visit the URL of the failed deployment and append `/_logs` so see the build error. In the case above, that would be https://test-8ashcdlew.now.sh/_logs
The logs of this deployment will contain the actual error which may help you to understand what went wrong.
### @zeit/ncc integration
Some of the builders use `@zeit/ncc` to bundle files before deployment. If you suspect an error with the bundling mechanism, you can run the `ncc` CLI with a couple modifications to the test.
For example if an error occurred in `now-node/test/fixtures/08-assets`
```
cd packages/now-node/test/fixtures/08-assets
yarn install
echo 'require("http").createServer(module.exports).listen(3000)' >> index.js
npx @zeit/ncc@0.20.1 build index.js --source-map
node dist
```
This will compile the test with the specific version of `ncc` and run the resulting file. If it fails here, then there is likely a bug in `ncc` and not the Builder.

View File

@@ -23,7 +23,10 @@ For the Canary Channel, publish the modified Builders to npm with the following:
yarn publish-canary
```
For the Stable Channel, you must cherry pick each commit from canary to master and then deploy the modified Builders:
For the Stable Channel, you must do the following:
- Cherry pick each commit from canary to master
- Verify that you are *in-sync* with canary (with the exception of the `version` line in `package.json`)
- Deploy the modified Builders
```
git checkout master
@@ -33,6 +36,7 @@ git cherry-pick <PR502_COMMIT_SHA>
git cherry-pick <PR503_COMMIT_SHA>
git cherry-pick <PR504_COMMIT_SHA>
# ... etc ...
git diff origin/canary
yarn publish-stable
```
@@ -41,3 +45,7 @@ After running this publish step, GitHub Actions will take care of publishing the
If for some reason GitHub Actions fails to publish the npm package, you may do so
manually by running `npm publish` from the package directory. Make sure to
use `npm publish --tag canary` if you are publishing a canary release!
### Contributing
See the [Contribution guidelines for this project](CONTRIBUTING.md), it also contains guidance on interpreting tests failures.

View File

@@ -1,6 +1,6 @@
{
"name": "@now/build-utils",
"version": "0.5.8",
"version": "0.7.0",
"license": "MIT",
"main": "./dist/index.js",
"types": "./dist/index.d.js",
@@ -9,8 +9,12 @@
"url": "https://github.com/zeit/now-builders.git",
"directory": "packages/now-build-utils"
},
"scripts": {
"build": "tsc",
"test": "tsc && jest",
"prepublishOnly": "tsc"
},
"dependencies": {
"@types/cross-spawn": "6.0.0",
"async-retry": "1.2.3",
"async-sema": "2.1.4",
"cross-spawn": "6.0.5",
@@ -18,24 +22,21 @@
"fs-extra": "7.0.0",
"glob": "7.1.3",
"into-stream": "5.0.0",
"memory-fs": "0.4.1",
"multistream": "2.1.1",
"node-fetch": "2.2.0",
"semver": "6.1.1",
"yazl": "2.4.3"
},
"scripts": {
"build": "tsc",
"test": "tsc && jest",
"prepublish": "tsc"
},
"devDependencies": {
"@types/async-retry": "^1.2.1",
"@types/cross-spawn": "6.0.0",
"@types/end-of-stream": "^1.4.0",
"@types/fs-extra": "^5.0.5",
"@types/glob": "^7.1.1",
"@types/node-fetch": "^2.1.6",
"@types/semver": "6.0.0",
"@types/yazl": "^2.4.1",
"execa": "^1.0.0",
"typescript": "3.3.4000"
"typescript": "3.5.2"
}
}

View File

@@ -0,0 +1,45 @@
import { intersects } from 'semver';
import { NodeVersion } from '../types';
const supportedOptions: NodeVersion[] = [
{ major: 10, range: '10.x', runtime: 'nodejs10.x' },
{ major: 8, range: '8.10.x', runtime: 'nodejs8.10' },
];
// This version should match Fargate's default in the PATH
// Today that is Node 8
export const defaultSelection = supportedOptions.find(
o => o.major === 8
) as NodeVersion;
export async function getSupportedNodeVersion(
engineRange?: string
): Promise<NodeVersion> {
let selection = defaultSelection;
if (!engineRange) {
console.log(
'missing `engines` in `package.json`, using default range: ' +
selection.range
);
} else {
const found = supportedOptions.some(o => {
// the array is already in order so return the first
// match which will be the newest version of node
selection = o;
return intersects(o.range, engineRange);
});
if (found) {
console.log(
'found `engines` in `package.json`, selecting range: ' + selection.range
);
} else {
throw new Error(
'found `engines` in `package.json` with an unsupported node range: ' +
engineRange +
'\nplease use `10.x` or `8.10.x` instead'
);
}
}
return selection;
}

View File

@@ -4,6 +4,8 @@ import path from 'path';
import spawn from 'cross-spawn';
import { SpawnOptions } from 'child_process';
import { deprecate } from 'util';
import { Meta, PackageJson, NodeVersion } from '../types';
import { getSupportedNodeVersion } from './node-version';
function spawnAsync(
command: string,
@@ -52,11 +54,32 @@ export async function runShellScript(fsPath: string) {
return true;
}
async function scanParentDirs(destPath: string, scriptName?: string) {
export function getSpawnOptions(
meta: Meta,
nodeVersion: NodeVersion
): SpawnOptions {
const opts = {
env: { ...process.env },
};
if (!meta.isDev) {
opts.env.PATH = `/node${nodeVersion.major}/bin:${opts.env.PATH}`;
}
return opts;
}
export async function getNodeVersion(destPath: string): Promise<NodeVersion> {
const { packageJson } = await scanParentDirs(destPath, true);
const range = packageJson && packageJson.engines && packageJson.engines.node;
return getSupportedNodeVersion(range);
}
async function scanParentDirs(destPath: string, readPackageJson = false) {
assert(path.isAbsolute(destPath));
let hasScript = false;
let hasPackageLockJson = false;
let packageJson: PackageJson | undefined;
let currentDestPath = destPath;
// eslint-disable-next-line no-constant-condition
@@ -65,13 +88,8 @@ async function scanParentDirs(destPath: string, scriptName?: string) {
// eslint-disable-next-line no-await-in-loop
if (await fs.pathExists(packageJsonPath)) {
// eslint-disable-next-line no-await-in-loop
if (scriptName) {
const packageJson = JSON.parse(
await fs.readFile(packageJsonPath, 'utf8')
);
hasScript = Boolean(
packageJson.scripts && scriptName && packageJson.scripts[scriptName]
);
if (readPackageJson) {
packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
}
// eslint-disable-next-line no-await-in-loop
hasPackageLockJson = await fs.pathExists(
@@ -85,13 +103,13 @@ async function scanParentDirs(destPath: string, scriptName?: string) {
currentDestPath = newDestPath;
}
return { hasScript, hasPackageLockJson };
return { hasPackageLockJson, packageJson };
}
export async function runNpmInstall(
destPath: string,
args: string[] = [],
cmd?: string
spawnOpts?: SpawnOptions
) {
assert(path.isAbsolute(destPath));
@@ -99,23 +117,19 @@ export async function runNpmInstall(
console.log(`installing to ${destPath}`);
const { hasPackageLockJson } = await scanParentDirs(destPath);
const opts: SpawnOptions = {
env: {
...process.env,
},
};
const opts = spawnOpts || { env: process.env };
if (hasPackageLockJson) {
commandArgs = args.filter(a => a !== '--prefer-offline');
await spawnAsync(
cmd || 'npm',
'npm',
commandArgs.concat(['install', '--unsafe-perm']),
destPath,
opts
);
} else {
await spawnAsync(
cmd || 'yarn',
'yarn',
commandArgs.concat(['--ignore-engines', '--cwd', destPath]),
destPath,
opts
@@ -129,9 +143,15 @@ export async function runPackageJsonScript(
opts?: SpawnOptions
) {
assert(path.isAbsolute(destPath));
const { hasScript, hasPackageLockJson } = await scanParentDirs(
const { packageJson, hasPackageLockJson } = await scanParentDirs(
destPath,
scriptName
true
);
const hasScript = Boolean(
packageJson &&
packageJson.scripts &&
scriptName &&
packageJson.scripts[scriptName]
);
if (!hasScript) return false;
@@ -152,7 +172,7 @@ export async function runPackageJsonScript(
}
/**
* installDependencies() is deprecated.
* @deprecate installDependencies() is deprecated.
* Please use runNpmInstall() instead.
*/
export const installDependencies = deprecate(

View File

@@ -20,6 +20,8 @@ import {
runPackageJsonScript,
runNpmInstall,
runShellScript,
getNodeVersion,
getSpawnOptions,
} from './fs/run-user-scripts';
import streamToBuffer from './fs/stream-to-buffer';
import shouldServe from './should-serve';
@@ -42,6 +44,8 @@ export {
runPackageJsonScript,
runNpmInstall,
runShellScript,
getNodeVersion,
getSpawnOptions,
streamToBuffer,
AnalyzeOptions,
BuildOptions,

View File

@@ -162,3 +162,28 @@ export interface ShouldServeOptions {
*/
config: Config;
}
export interface PackageJson {
name: string;
version: string;
engines?: {
[key: string]: string;
node: string;
npm: string;
};
scripts?: {
[key: string]: string;
};
dependencies?: {
[key: string]: string;
};
devDependencies?: {
[key: string]: string;
};
}
export interface NodeVersion {
major: number;
range: string;
runtime: string;
}

View File

@@ -6,6 +6,10 @@ const execa = require('execa');
const assert = require('assert');
const { glob, download } = require('../');
const { createZip } = require('../dist/lambda');
const {
getSupportedNodeVersion,
defaultSelection,
} = require('../dist/fs/node-version');
const {
packAndDeploy,
@@ -64,6 +68,52 @@ it('should create zip files with symlinks properly', async () => {
assert(aStat.isFile());
});
it('should only match supported node versions', () => {
expect(getSupportedNodeVersion('10.x')).resolves.toHaveProperty('major', 10);
expect(getSupportedNodeVersion('8.10.x')).resolves.toHaveProperty('major', 8);
expect(getSupportedNodeVersion('8.11.x')).rejects.toThrow();
expect(getSupportedNodeVersion('6.x')).rejects.toThrow();
expect(getSupportedNodeVersion('999.x')).rejects.toThrow();
expect(getSupportedNodeVersion('foo')).rejects.toThrow();
expect(getSupportedNodeVersion('')).resolves.toBe(defaultSelection);
expect(getSupportedNodeVersion(null)).resolves.toBe(defaultSelection);
expect(getSupportedNodeVersion(undefined)).resolves.toBe(defaultSelection);
});
it('should match all semver ranges', () => {
// See https://docs.npmjs.com/files/package.json#engines
expect(getSupportedNodeVersion('10.0.0')).resolves.toHaveProperty(
'major',
10,
);
expect(getSupportedNodeVersion('10.x')).resolves.toHaveProperty('major', 10);
expect(getSupportedNodeVersion('>=10')).resolves.toHaveProperty('major', 10);
expect(getSupportedNodeVersion('>=10.3.0')).resolves.toHaveProperty(
'major',
10,
);
expect(getSupportedNodeVersion('8.5.0 - 10.5.0')).resolves.toHaveProperty(
'major',
10,
);
expect(getSupportedNodeVersion('>=9.0.0')).resolves.toHaveProperty(
'major',
10,
);
expect(getSupportedNodeVersion('>=9.5.0 <=10.5.0')).resolves.toHaveProperty(
'major',
10,
);
expect(getSupportedNodeVersion('~10.5.0')).resolves.toHaveProperty(
'major',
10,
);
expect(getSupportedNodeVersion('^10.5.0')).resolves.toHaveProperty(
'major',
10,
);
});
// own fixtures
const fixturesPath = path.resolve(__dirname, 'fixtures');

View File

@@ -128,18 +128,18 @@ Learn more: https://zeit.co/docs/v2/deployments/official-builders/go-now-go/#ent
const entrypointDirname = dirname(downloadedFiles[entrypoint].fsPath);
let isGoModExist = false;
let goModPath = '';
let goModPathArr: string[] = [];
let isGoModInRootDir = false;
for (const file of Object.keys(downloadedFiles)) {
const fileDirname = dirname(downloadedFiles[file].fsPath);
if (file === 'go.mod') {
isGoModExist = true;
isGoModInRootDir = true;
goModPath = fileDirname;
goModPathArr = goModPath.split(sep);
} else if (file.includes('go.mod')) {
isGoModExist = true;
if (entrypointDirname === fileDirname) {
isGoModExist = true;
goModPath = fileDirname;
goModPathArr = goModPath.split(sep);
break;
}
}
}
@@ -202,14 +202,28 @@ Learn more: https://zeit.co/docs/v2/deployments/official-builders/go-now-go/#ent
if (isGoModExist) {
const goModContents = await readFile(join(goModPath, 'go.mod'), 'utf8');
const usrModName = goModContents.split('\n')[0].split(' ')[1];
goPackageName = `${usrModName}/${packageName}`;
if (entrypointArr.length > 1 && isGoModInRootDir) {
let cleanPackagePath = [...entrypointArr];
cleanPackagePath.pop();
goPackageName = `${usrModName}/${cleanPackagePath.join('/')}`;
} else {
goPackageName = `${usrModName}/${packageName}`;
}
}
const mainModGoContents = modMainGoContents
.replace('__NOW_HANDLER_PACKAGE_NAME', goPackageName)
.replace('__NOW_HANDLER_FUNC_NAME', goFuncName);
if (goModPathArr.length > 1) {
if (meta.isDev && isGoModExist && isGoModInRootDir) {
await writeFile(
join(dirname(downloadedFiles['now.json'].fsPath), mainModGoFileName),
mainModGoContents
);
} else if (isGoModExist && isGoModInRootDir) {
await writeFile(join(srcPath, mainModGoFileName), mainModGoContents);
} else if (isGoModExist && !isGoModInRootDir) {
// using `go.mod` path to write main__mod__.go
await writeFile(join(goModPath, mainModGoFileName), mainModGoContents);
} else {
@@ -252,21 +266,28 @@ Learn more: https://zeit.co/docs/v2/deployments/official-builders/go-now-go/#ent
throw err;
}
let baseGoModPath = '';
if (meta.isDev && isGoModExist && isGoModInRootDir) {
baseGoModPath = dirname(downloadedFiles['now.json'].fsPath);
} else if (isGoModExist && isGoModInRootDir) {
baseGoModPath = srcPath;
} else if (isGoModExist && !isGoModInRootDir) {
baseGoModPath = goModPath;
} else {
baseGoModPath = entrypointDirname;
}
if (meta.isDev) {
let entrypointDir = entrypointDirname;
if (goModPathArr.length > 1) {
entrypointDir = goModPath;
}
const isGoModBk = await pathExists(join(entrypointDir, 'go.mod.bk'));
const isGoModBk = await pathExists(join(baseGoModPath, 'go.mod.bk'));
if (isGoModBk) {
await move(
join(entrypointDir, 'go.mod.bk'),
join(entrypointDir, 'go.mod'),
join(baseGoModPath, 'go.mod.bk'),
join(baseGoModPath, 'go.mod'),
{ overwrite: true }
);
await move(
join(entrypointDir, 'go.sum.bk'),
join(entrypointDir, 'go.sum'),
join(baseGoModPath, 'go.sum.bk'),
join(baseGoModPath, 'go.sum'),
{ overwrite: true }
);
}
@@ -283,8 +304,7 @@ Learn more: https://zeit.co/docs/v2/deployments/official-builders/go-now-go/#ent
console.log('Running `go build`...');
const destPath = join(outDir, 'handler');
const isGoModInRootDir = goModPathArr.length === 1;
const baseGoModPath = isGoModInRootDir ? entrypointDirname : goModPath;
try {
let src = [join(baseGoModPath, mainModGoFileName)];

View File

@@ -1,6 +1,6 @@
{
"name": "@now/go",
"version": "0.5.1",
"version": "0.5.2",
"license": "MIT",
"repository": {
"type": "git",
@@ -31,6 +31,6 @@
"@types/fs-extra": "^5.0.5",
"@types/node-fetch": "^2.3.0",
"@types/tar": "^4.0.0",
"typescript": "^3.4.2"
"typescript": "3.5.2"
}
}

View File

@@ -0,0 +1,12 @@
package nested
import (
"fmt"
"net/http"
"with-nested/shared"
)
// Handler func
func Handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, shared.Say("lol:RANDOMNESS_PLACEHOLDER"))
}

View File

@@ -0,0 +1,3 @@
module with-nested
go 1.12

View File

@@ -0,0 +1,5 @@
{
"version": 2,
"builds": [{ "src": "api/nested/*.go", "use": "@now/go" }],
"probes": [{ "path": "/api/nested", "mustContain": "RANDOMNESS_PLACEHOLDER" }]
}

View File

@@ -0,0 +1,6 @@
package shared
// Say func
func Say(text string) string {
return text
}

View File

@@ -0,0 +1,11 @@
package handler
import (
"fmt"
"net/http"
)
// Handler func
func Handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "hello:RANDOMNESS_PLACEHOLDER")
}

View File

@@ -0,0 +1,5 @@
{
"version": 2,
"builds": [{ "src": "api/**/*.go", "use": "@now/go" }],
"probes": [{ "path": "/api", "mustContain": "RANDOMNESS_PLACEHOLDER" }]
}

View File

@@ -0,0 +1,3 @@
module other-folder
go 1.12

View File

@@ -0,0 +1,11 @@
package handler
import (
"fmt"
"net/http"
)
// Handler func
func Handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "hello:RANDOMNESS_PLACEHOLDER")
}

View File

@@ -1,6 +1,6 @@
{
"name": "@now/next",
"version": "0.4.2",
"version": "0.5.0",
"license": "MIT",
"main": "./dist/index",
"scripts": {
@@ -14,7 +14,7 @@
"directory": "packages/now-next"
},
"dependencies": {
"@now/node-bridge": "^1.2.0",
"@now/node-bridge": "^1.2.1",
"fs-extra": "^7.0.0",
"get-port": "^5.0.0",
"resolve-from": "^5.0.0",
@@ -28,6 +28,6 @@
"@types/resolve-from": "^5.0.1",
"@types/semver": "^6.0.0",
"jest": "^24.7.1",
"typescript": "^3.4.3"
"typescript": "3.5.2"
}
}

View File

@@ -21,6 +21,8 @@ import {
PrepareCacheOptions,
runNpmInstall,
runPackageJsonScript,
getNodeVersion,
getSpawnOptions,
} from '@now/build-utils';
import nextLegacyVersions from './legacy-versions';
@@ -171,6 +173,9 @@ export const build = async ({
console.log(`${name} Downloading user files...`);
await download(files, workPath, meta);
const nodeVersion = await getNodeVersion(entryPath);
const spawnOpts = getSpawnOptions(meta, nodeVersion);
const pkg = await readPackageJson(entryPath);
const nextVersion = getNextVersion(pkg);
@@ -190,7 +195,7 @@ export const build = async ({
// If this is the initial build, we want to start the server
if (!urls[entrypoint]) {
console.log(`${name} Installing dependencies...`);
await runNpmInstall(entryPath, ['--prefer-offline']);
await runNpmInstall(entryPath, ['--prefer-offline'], spawnOpts);
if (!process.env.NODE_ENV) {
process.env.NODE_ENV = 'development';
@@ -277,20 +282,22 @@ export const build = async ({
}
console.log('installing dependencies...');
await runNpmInstall(entryPath, ['--prefer-offline']);
await runNpmInstall(entryPath, ['--prefer-offline'], spawnOpts);
console.log('running user script...');
const memoryToConsume = Math.floor(os.totalmem() / 1024 ** 2) - 128;
await runPackageJsonScript(entryPath, 'now-build', {
env: {
...process.env,
NODE_OPTIONS: `--max_old_space_size=${memoryToConsume}`,
},
} as SpawnOptions);
const buildSpawnOptions = { ...spawnOpts };
const env = { ...buildSpawnOptions.env } as any;
env.NODE_OPTIONS = `--max_old_space_size=${memoryToConsume}`;
await runPackageJsonScript(entryPath, 'now-build', buildSpawnOptions);
if (isLegacy) {
console.log('running npm install --production...');
await runNpmInstall(entryPath, ['--prefer-offline', '--production']);
await runNpmInstall(
entryPath,
['--prefer-offline', '--production'],
spawnOpts
);
}
if (process.env.NPM_AUTH_TOKEN) {

View File

@@ -1,6 +1,6 @@
{
"name": "@now/node-bridge",
"version": "1.2.0",
"version": "1.2.1",
"license": "MIT",
"main": "./index.js",
"repository": {
@@ -21,6 +21,6 @@
"@types/aws-lambda": "8.10.19",
"@types/node": "11.9.4",
"jest": "24.1.0",
"typescript": "3.3.3"
"typescript": "3.5.2"
}
}

View File

@@ -1,15 +1,17 @@
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'); // eslint-disable-line import/no-extraneous-dependencies
const path = require('path');
const {
FileBlob,
FileFsRef,
download,
createLambda,
glob,
runNpmInstall,
runPackageJsonScript,
} = require('@now/build-utils/fs/run-user-scripts.js'); // eslint-disable-line import/no-extraneous-dependencies
const { shouldServe } = require('@now/build-utils'); // eslint-disable-line import/no-extraneous-dependencies
getNodeVersion,
getSpawnOptions,
shouldServe,
} = require('@now/build-utils'); // eslint-disable-line import/no-extraneous-dependencies
/** @typedef { import('@now/build-utils/file-ref') } FileRef */
/** @typedef {{[filePath: string]: FileRef}} Files */
@@ -38,8 +40,15 @@ async function downloadInstallAndBundle(
console.log("installing dependencies for user's code...");
const entrypointFsDirname = path.join(workPath, path.dirname(entrypoint));
await runNpmInstall(entrypointFsDirname, npmArguments);
return [downloadedFiles, entrypointFsDirname];
const nodeVersion = await getNodeVersion(entrypointFsDirname);
const spawnOpts = getSpawnOptions(meta, nodeVersion);
await runNpmInstall(entrypointFsDirname, npmArguments, spawnOpts);
return {
downloadedFiles,
entrypointFsDirname,
spawnOpts,
nodeVersion,
};
}
async function compile(workPath, downloadedFiles, entrypoint, config) {
@@ -101,9 +110,14 @@ exports.config = {
* @returns {Promise<Files>}
*/
exports.build = async ({
files, entrypoint, config, workPath, meta,
files, entrypoint, config, workPath, meta = {},
}) => {
const [downloadedFiles, entrypointFsDirname] = await downloadInstallAndBundle(
const {
downloadedFiles,
entrypointFsDirname,
spawnOptions,
nodeVersion,
} = await downloadInstallAndBundle(
{
files,
entrypoint,
@@ -114,7 +128,7 @@ exports.build = async ({
);
console.log('running user script...');
await runPackageJsonScript(entrypointFsDirname, 'now-build');
await runPackageJsonScript(entrypointFsDirname, 'now-build', spawnOptions);
console.log('preparing lambda files...');
let preparedFiles;
@@ -146,7 +160,7 @@ exports.build = async ({
const lambda = await createLambda({
files: { ...preparedFiles, ...launcherFiles },
handler: 'launcher.launcher',
runtime: 'nodejs8.10',
runtime: nodeVersion.runtime,
});
return { [entrypoint]: lambda };

View File

@@ -1,6 +1,6 @@
{
"name": "@now/node-server",
"version": "0.7.5",
"version": "0.8.0",
"license": "MIT",
"repository": {
"type": "git",
@@ -8,7 +8,7 @@
"directory": "packages/now-node-server"
},
"dependencies": {
"@now/node-bridge": "^1.2.0",
"@now/node-bridge": "^1.2.1",
"@zeit/ncc": "0.18.5",
"fs-extra": "7.0.1"
},

View File

@@ -1,6 +1,6 @@
{
"name": "@now/node",
"version": "0.9.0",
"version": "0.10.0",
"license": "MIT",
"main": "./dist/index",
"repository": {
@@ -9,7 +9,7 @@
"directory": "packages/now-node"
},
"dependencies": {
"@now/node-bridge": "^1.2.0",
"@now/node-bridge": "^1.2.1",
"@types/node": "*",
"@zeit/ncc": "0.18.5",
"@zeit/ncc-watcher": "1.0.3",
@@ -31,6 +31,6 @@
"cookie": "0.4.0",
"node-fetch": "2.6.0",
"test-listen": "1.1.0",
"typescript": "3.3.3"
"typescript": "3.5.2"
}
}

View File

@@ -12,6 +12,8 @@ import {
createLambda,
runNpmInstall,
runPackageJsonScript,
getNodeVersion,
getSpawnOptions,
PrepareCacheOptions,
BuildOptions,
shouldServe,
@@ -58,10 +60,12 @@ async function downloadInstallAndBundle({
console.log("installing dependencies for user's code...");
const entrypointFsDirname = join(workPath, dirname(entrypoint));
await runNpmInstall(entrypointFsDirname, ['--prefer-offline']);
const nodeVersion = await getNodeVersion(entrypointFsDirname);
const spawnOpts = getSpawnOptions(meta, nodeVersion);
await runNpmInstall(entrypointFsDirname, ['--prefer-offline'], spawnOpts);
const entrypointPath = downloadedFiles[entrypoint].fsPath;
return { entrypointPath, entrypointFsDirname };
return { entrypointPath, entrypointFsDirname, nodeVersion, spawnOpts };
}
async function compile(
@@ -178,6 +182,8 @@ export async function build({
const {
entrypointPath,
entrypointFsDirname,
nodeVersion,
spawnOpts,
} = await downloadInstallAndBundle({
files,
entrypoint,
@@ -186,7 +192,7 @@ export async function build({
});
console.log('running user script...');
await runPackageJsonScript(entrypointFsDirname, 'now-build');
await runPackageJsonScript(entrypointFsDirname, 'now-build', spawnOpts);
console.log('compiling entrypoint with ncc...');
const { preparedFiles, watch } = await compile(
@@ -233,7 +239,7 @@ export async function build({
...launcherFiles,
},
handler: 'launcher.launcher',
runtime: 'nodejs8.10',
runtime: nodeVersion.runtime,
});
const output = { [entrypoint]: lambda };

View File

@@ -0,0 +1,3 @@
module.exports = (req, res) => {
res.end(`RANDOMNESS_PLACEHOLDER:${process.versions.node}`);
};

View File

@@ -0,0 +1,3 @@
{
"name": "missing-engines-key-on-purpose"
}

View File

@@ -0,0 +1,3 @@
module.exports = (req, res) => {
res.end(`RANDOMNESS_PLACEHOLDER:${process.versions.node}`);
};

View File

@@ -0,0 +1,5 @@
{
"engines": {
"node": "10.5.0"
}
}

View File

@@ -0,0 +1,3 @@
module.exports = (req, res) => {
res.end(`RANDOMNESS_PLACEHOLDER:${process.versions.node}`);
};

View File

@@ -0,0 +1,5 @@
{
"engines": {
"node": ">=10.0.0"
}
}

View File

@@ -0,0 +1,3 @@
module.exports = (req, res) => {
res.end(`RANDOMNESS_PLACEHOLDER:${process.versions.node}`);
};

View File

@@ -0,0 +1,5 @@
{
"engines": {
"node": "10.x"
}
}

View File

@@ -0,0 +1,11 @@
{
"version": 2,
"builds": [{ "src": "**/*.js", "use": "@now/node" }],
"probes": [
{ "path": "/empty", "mustContain": "RANDOMNESS_PLACEHOLDER:8" },
{ "path": "/exact", "mustContain": "RANDOMNESS_PLACEHOLDER:10" },
{ "path": "/greater", "mustContain": "RANDOMNESS_PLACEHOLDER:10" },
{ "path": "/major", "mustContain": "RANDOMNESS_PLACEHOLDER:10" },
{ "path": "/range", "mustContain": "RANDOMNESS_PLACEHOLDER:10" }
]
}

View File

@@ -0,0 +1,3 @@
module.exports = (req, res) => {
res.end(`RANDOMNESS_PLACEHOLDER:${process.versions.node}`);
};

View File

@@ -0,0 +1,5 @@
{
"engines": {
"node": "10.x"
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@now/optipng",
"version": "0.6.2",
"version": "0.6.3",
"license": "MIT",
"main": "./dist/index",
"files": [
@@ -21,6 +21,6 @@
},
"devDependencies": {
"@types/node": "11.9.4",
"typescript": "3.3.3"
"typescript": "3.5.2"
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@now/python",
"version": "0.2.7",
"version": "0.2.8",
"main": "./dist/index.js",
"license": "MIT",
"files": [
@@ -23,6 +23,6 @@
},
"devDependencies": {
"@types/execa": "^0.9.0",
"typescript": "3.3.4000"
"typescript": "3.5.2"
}
}

View File

@@ -104,7 +104,6 @@ export const build = async ({
workPath = destNow;
}
const foundLockFile = 'Pipfile.lock' in downloadedFiles;
const pyUserBase = await getWriteableDirectory();
process.env.PYTHONUSERBASE = pyUserBase;
const pipPath = 'pip3';
@@ -129,17 +128,25 @@ export const build = async ({
await pipInstall(pipPath, workPath, 'werkzeug');
await pipInstall(pipPath, workPath, 'requests');
if (foundLockFile) {
let fsFiles = await glob('**', workPath);
const entryDirectory = dirname(entrypoint);
const pipfileLockDir = fsFiles[join(entryDirectory, 'Pipfile.lock')]
? join(workPath, entryDirectory)
: fsFiles['Pipfile.lock']
? workPath
: null;
if (pipfileLockDir) {
console.log('found "Pipfile.lock"');
// Install pipenv.
await pipInstallUser(pipPath, ' pipenv_to_requirements');
await pipenvInstall(pyUserBase, workPath);
await pipenvInstall(pyUserBase, pipfileLockDir);
}
const fsFiles = await glob('**', workPath);
const entryDirectory = dirname(entrypoint);
fsFiles = await glob('**', workPath);
const requirementsTxt = join(entryDirectory, 'requirements.txt');
if (fsFiles[requirementsTxt]) {

View File

@@ -0,0 +1,12 @@
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true
[dev-packages]
[packages]
cowpy = "*"
[requires]
python_version = "3.7"

View File

@@ -0,0 +1,29 @@
{
"_meta": {
"hash": {
"sha256": "841b49dd1836f7373490faa2a1d6abb4ecd53c0b45e8196df7c6e852e21f458b"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.7"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
}
]
},
"default": {
"cowpy": {
"hashes": [
"sha256:1bdc61d107df02fd34a9241f2220d0704a01d8ce16bed8bff3512a34a0efa56a",
"sha256:91a861bfbfa644dfdba5b2250d141b2227a94f61d4dcbeaf7653524d048935a9"
],
"index": "pypi",
"version": "==1.1.0"
}
},
"develop": {}
}

View File

@@ -0,0 +1,13 @@
from http.server import BaseHTTPRequestHandler
from cowpy import cow
class handler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-type', 'text/plain')
self.end_headers()
message = cow.Cowacter().milk('pip:RANDOMNESS_PLACEHOLDER')
self.wfile.write(message.encode())
return

View File

@@ -0,0 +1,5 @@
{
"version": 2,
"builds": [{ "src": "**/**.py", "use": "@now/python" }],
"probes": [{ "path": "/", "mustContain": "pip:RANDOMNESS_PLACEHOLDER" }]
}

View File

@@ -0,0 +1,12 @@
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true
[dev-packages]
[packages]
cowpy = "*"
[requires]
python_version = "3.7"

View File

@@ -0,0 +1,29 @@
{
"_meta": {
"hash": {
"sha256": "841b49dd1836f7373490faa2a1d6abb4ecd53c0b45e8196df7c6e852e21f458b"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.7"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
}
]
},
"default": {
"cowpy": {
"hashes": [
"sha256:1bdc61d107df02fd34a9241f2220d0704a01d8ce16bed8bff3512a34a0efa56a",
"sha256:91a861bfbfa644dfdba5b2250d141b2227a94f61d4dcbeaf7653524d048935a9"
],
"index": "pypi",
"version": "==1.1.0"
}
},
"develop": {}
}

View File

@@ -0,0 +1,13 @@
from http.server import BaseHTTPRequestHandler
from cowpy import cow
class handler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-type', 'text/plain')
self.end_headers()
message = cow.Cowacter().milk('pip:RANDOMNESS_PLACEHOLDER')
self.wfile.write(message.encode())
return

View File

@@ -0,0 +1,5 @@
{
"version": 2,
"builds": [{ "src": "**/**.py", "use": "@now/python" }],
"probes": [{ "path": "/local", "mustContain": "pip:RANDOMNESS_PLACEHOLDER" }]
}

View File

@@ -4,6 +4,9 @@
"routes": [{ "src": "/another", "dest": "custom.py" }],
"probes": [
{ "path": "/?hello=/", "mustContain": "path: query: {'hello': '/'}" },
{ "path": "/another?hello=/", "mustContain": "path: another query: {'hello': '/'}" }
{
"path": "/another?hello=/",
"mustContain": "path: another query: {'hello': '/'}"
}
]
}

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

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

220
packages/now-ruby/index.ts Normal file
View File

@@ -0,0 +1,220 @@
import { join, dirname } from 'path';
import execa from 'execa';
import {
ensureDir,
move,
remove,
pathExists,
readFile,
writeFile,
} from 'fs-extra';
import {
download,
getWriteableDirectory,
glob,
createLambda,
BuildOptions,
} from '@now/build-utils';
import { installBundler } from './install-ruby';
const REQUIRED_VENDOR_DIR = 'vendor/bundle/ruby/2.5.0';
async function matchPaths(
configPatterns: string | string[] | undefined,
workPath: string
) {
const patterns =
typeof configPatterns === 'string' ? [configPatterns] : configPatterns;
if (!patterns) {
return [];
}
const patternPaths = await Promise.all(
patterns.map(async pattern => {
const files = await glob(pattern, workPath);
return Object.keys(files);
})
);
return patternPaths.reduce((a, b) => a.concat(b), []);
}
async function bundleInstall(
bundlePath: string,
bundleDir: string,
gemfilePath: string
) {
console.log(`running "bundle install --deployment"...`);
const bundleAppConfig = await getWriteableDirectory();
try {
await execa(
bundlePath,
[
'install',
'--deployment',
'--gemfile',
gemfilePath,
'--path',
bundleDir,
],
{
stdio: 'inherit',
env: {
BUNDLE_SILENCE_ROOT_WARNING: '1',
BUNDLE_APP_CONFIG: bundleAppConfig,
},
}
);
} catch (err) {
console.log(`failed to run "bundle install --deployment"...`);
throw err;
}
}
export const config = {
maxLambdaSize: '5mb',
};
export const build = async ({
workPath,
files,
entrypoint,
config,
}: BuildOptions) => {
console.log('downloading files...');
// eslint-disable-next-line no-param-reassign
files = await download(files, workPath);
const { gemHome, bundlerPath } = await installBundler();
process.env.GEM_HOME = gemHome;
const fsFiles = await glob('**', workPath);
const entryDirectory = dirname(entrypoint);
const fsEntryDirectory = dirname(fsFiles[entrypoint].fsPath);
// check for an existing vendor directory
console.log(
'checking for existing vendor directory at',
'"' + REQUIRED_VENDOR_DIR + '"'
);
const vendorDir = join(workPath, REQUIRED_VENDOR_DIR);
const bundleDir = join(workPath, 'vendor/bundle');
const relativeVendorDir = join(fsEntryDirectory, REQUIRED_VENDOR_DIR);
let hasRootVendorDir = await pathExists(vendorDir);
let hasRelativeVendorDir = await pathExists(relativeVendorDir);
let hasVendorDir = hasRootVendorDir || hasRelativeVendorDir;
if (hasRelativeVendorDir) {
if (hasRootVendorDir) {
console.log(
'found two vendor directories, choosing the vendor directory relative to entrypoint'
);
} else {
console.log('found vendor directory relative to entrypoint');
}
// vendor dir must be at the root for lambda to find it
await move(relativeVendorDir, vendorDir);
} else if (hasRootVendorDir) {
console.log('found vendor directory in project root');
}
await ensureDir(vendorDir);
// no vendor directory, check for Gemfile to install
if (!hasVendorDir) {
const gemFile = join(entryDirectory, 'Gemfile');
if (fsFiles[gemFile]) {
console.log(
'did not find a vendor directory but found a Gemfile, bundling gems...'
);
const gemfilePath = fsFiles[gemFile].fsPath;
// try installing. this won't work if native extesions are required.
// if that's the case, gems should be vendored locally before deploying.
try {
await bundleInstall(bundlerPath, bundleDir, gemfilePath);
} catch (err) {
console.log(
'unable to build gems from Gemfile. vendor the gems locally with "bundle install --deployment" and retry.'
);
throw err;
}
}
} else {
console.log('found vendor directory, skipping "bundle install"...');
}
// try to remove gem cache to slim bundle size
try {
await remove(join(vendorDir, 'cache'));
} catch (e) {}
const originalRbPath = join(__dirname, '..', 'now_init.rb');
const originalNowHandlerRbContents = await readFile(originalRbPath, 'utf8');
// will be used on `require_relative '$here'` or for loading rack config.ru file
// for example, `require_relative 'api/users'`
console.log('entrypoint is', entrypoint);
const userHandlerFilePath = entrypoint.replace(/\.rb$/, '');
const nowHandlerRbContents = originalNowHandlerRbContents.replace(
/__NOW_HANDLER_FILENAME/g,
userHandlerFilePath
);
// in order to allow the user to have `server.rb`, we need our `server.rb` to be called
// somethig else
const nowHandlerRbFilename = 'now__handler__ruby';
await writeFile(
join(workPath, `${nowHandlerRbFilename}.rb`),
nowHandlerRbContents
);
const outputFiles = await glob('**', workPath);
// static analysis is impossible with ruby.
// instead, provide `includeFiles` and `excludeFiles` config options to reduce bundle size.
if (config && (config.includeFiles || config.excludeFiles)) {
const includedPaths = await matchPaths(config.includeFiles, workPath);
const excludedPaths = await matchPaths(
<string | string[]>config.excludeFiles,
workPath
);
for (let i = 0; i < excludedPaths.length; i++) {
// whitelist includeFiles
if (includedPaths.includes(excludedPaths[i])) {
continue;
}
// whitelist handler
if (excludedPaths[i] === `${nowHandlerRbFilename}.rb`) {
continue;
}
// whitelist vendor directory
if (excludedPaths[i].startsWith(REQUIRED_VENDOR_DIR)) {
continue;
}
delete outputFiles[excludedPaths[i]];
}
}
const lambda = await createLambda({
files: outputFiles,
handler: `${nowHandlerRbFilename}.now__handler`,
runtime: 'ruby2.5',
environment: {},
});
return {
[entrypoint]: lambda,
};
};

View File

@@ -0,0 +1,66 @@
import { join } from 'path';
import execa from 'execa';
import { getWriteableDirectory } from '@now/build-utils';
const RUBY_VERSION = '2.5.3';
async function installRuby(version: string = RUBY_VERSION) {
const baseDir = await getWriteableDirectory();
const rubyDir = join(baseDir, 'ruby');
const rubyBuildDir = join(baseDir, 'ruby-build');
await execa(
'yum',
[
'install',
'-y',
'git',
'gcc',
'make',
'tar',
'bzip2',
'readline-devel',
'openssl-devel',
'ruby-devel',
'zlib-devel',
],
{ stdio: 'inherit' }
);
await execa(
'git',
['clone', 'git://github.com/rbenv/ruby-build.git', rubyBuildDir],
{ stdio: 'inherit' }
);
await execa(join(rubyBuildDir, 'bin', 'ruby-build'), [version, rubyDir], {
stdio: 'inherit',
});
return {
gemHome: rubyDir,
rubyPath: join(rubyDir, 'bin', 'ruby'),
gemPath: join(rubyDir, 'bin', 'gem'),
};
}
// downloads and installs `bundler` (respecting
// process.env.GEM_HOME), and returns
// the absolute path to it
export async function installBundler() {
console.log('installing ruby...');
const { gemHome, rubyPath, gemPath } = await installRuby();
console.log('installing bundler...');
await execa(gemPath, ['install', 'bundler', '--no-document'], {
stdio: 'inherit',
env: {
GEM_HOME: gemHome,
},
});
return {
gemHome,
rubyPath,
gemPath,
bundlerPath: join(gemHome, 'bin', 'bundler'),
};
}

87
packages/now-ruby/now_init.rb Executable file
View File

@@ -0,0 +1,87 @@
require 'tmpdir'
require 'webrick'
require 'net/http'
require 'base64'
require 'json'
$entrypoint = '__NOW_HANDLER_FILENAME'
ENV['RAILS_ENV'] ||= 'production'
ENV['RAILS_LOG_TO_STDOUT'] ||= '1'
def rack_handler(httpMethod, path, body, headers)
require 'rack'
app, _ = Rack::Builder.parse_file($entrypoint)
server = Rack::MockRequest.new app
env = headers.transform_keys { |k| k.split('-').join('_').prepend('HTTP_').upcase }
res = server.request(httpMethod, path, env.merge({ :input => body }))
{
:statusCode => res.status,
:headers => res.original_headers,
:body => res.body,
}
end
def webrick_handler(httpMethod, path, body, headers)
require_relative $entrypoint
if not Object.const_defined?('Handler')
return { :statusCode => 500, :body => 'Handler not defined in lambda' }
end
host = '0.0.0.0'
port = 3000
server = WEBrick::HTTPServer.new :BindAddress => host, :Port => port
if Handler.is_a?(Proc)
server.mount_proc '/', Handler
else
server.mount '/', Handler
end
th = Thread.new(server) do |server|
server.start
end
http = Net::HTTP.new(host, port)
res = http.send_request(httpMethod, path, body, headers)
Signal.list.keys.each do |sig|
begin
Signal.trap(sig, cleanup)
rescue
end
end
server.shutdown
Thread.kill(th)
{
:statusCode => res.code.to_i,
:headers => res.each_capitalized.to_h,
:body => res.body,
}
end
def now__handler(event:, context:)
payload = JSON.parse(event['body'])
path = payload['path']
headers = payload['headers']
httpMethod = payload['method']
encoding = payload['encoding']
body = payload['body']
if (not body.nil? and not body.empty?) and (not encoding.nil? and encoding == 'base64')
body = Base64.decode64(body)
end
if $entrypoint.end_with? '.ru'
return rack_handler(httpMethod, path, body, headers)
end
return webrick_handler(httpMethod, path, body, headers)
end

26
packages/now-ruby/package.json Executable file
View File

@@ -0,0 +1,26 @@
{
"name": "@now/ruby",
"author": "Nathan Cahill <nathan@nathancahill.com>",
"version": "0.1.0",
"main": "./dist/index",
"files": [
"dist",
"now_init.rb"
],
"repository": {
"type": "git",
"url": "https://github.com/zeit/now-builders.git",
"directory": "packages/now-ruby"
},
"license": "MIT",
"scripts": {
"build": "tsc",
"test": "tsc && jest",
"prepublishOnly": "tsc"
},
"dependencies": {
"execa": "^1.0.0",
"fs-extra": "^7.0.1",
"typescript": "3.5.2"
}
}

View File

@@ -0,0 +1,5 @@
# frozen_string_literal: true
source "https://rubygems.org"
gem "cowsay", "~> 0.3.0"

View File

@@ -0,0 +1,13 @@
GEM
remote: https://rubygems.org/
specs:
cowsay (0.3.0)
PLATFORMS
ruby
DEPENDENCIES
cowsay (~> 0.3.0)
BUNDLED WITH
2.0.1

View File

@@ -0,0 +1,10 @@
require 'webrick'
require 'cowsay'
class Handler < WEBrick::HTTPServlet::AbstractServlet
def do_GET req, res
res.status = 200
res['Content-Type'] = 'text/plain'
res.body = Cowsay.say('gem:RANDOMNESS_PLACEHOLDER', 'cow')
end
end

View File

@@ -0,0 +1,5 @@
{
"version": 2,
"builds": [{ "src": "index.rb", "use": "@now/ruby" }],
"probes": [{ "path": "/", "mustContain": "gem:RANDOMNESS_PLACEHOLDER" }]
}

View File

@@ -0,0 +1,10 @@
require 'webrick'
require 'cowsay'
class Handler < WEBrick::HTTPServlet::AbstractServlet
def do_GET req, res
res.status = 200
res['Content-Type'] = 'text/plain'
res.body = Cowsay.say('gem:RANDOMNESS_PLACEHOLDER', 'cow')
end
end

View File

@@ -0,0 +1,5 @@
{
"version": 2,
"builds": [{ "src": "index.rb", "use": "@now/ruby" }],
"probes": [{ "path": "/", "mustContain": "gem:RANDOMNESS_PLACEHOLDER" }]
}

View File

@@ -0,0 +1,27 @@
#!/usr/bin/env ruby
#
# This file was generated by RubyGems.
#
# The application 'cowsay' is installed as part of a gem, and
# this file is here to facilitate running it.
#
require 'rubygems'
version = ">= 0.a"
str = ARGV.first
if str
str = str.b[/\A_(.*)_\z/, 1]
if str and Gem::Version.correct?(str)
version = str
ARGV.shift
end
end
if Gem.respond_to?(:activate_bin_path)
load Gem.activate_bin_path('cowsay', 'cowsay', version)
else
gem "cowsay", version
load Gem.bin_path("cowsay", "cowsay", version)
end

View File

@@ -0,0 +1,20 @@
*.gem
*.rbc
*.swp
.DS_Store
.bundle
.config
.vagrant
.yardoc
Gemfile.lock
InstalledFiles
_yardoc
coverage
doc/
lib/bundler/man
pkg
rdoc
spec/reports
test/tmp
test/version_tmp
tmp

View File

@@ -0,0 +1,4 @@
source 'https://rubygems.org'
# Specify your gem's dependencies in zerobuf.gemspec
gemspec

View File

@@ -0,0 +1,23 @@
Copyright 2012 MoneyDesktop Inc.
Copyright Cowsay contributors https://github.com/moneydesktop/cowsay/contributors
MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,21 @@
# Cowsay
ASCII art avatars emote your messages
## Installation
Add this line to your application's Gemfile:
gem install cowsay
And then execute:
$ cowsay 'Hello world!'
## Contributing
1. Fork it
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create new Pull Request

View File

@@ -0,0 +1 @@
require 'bundler/gem_tasks'

View File

@@ -0,0 +1,28 @@
#!/usr/bin/env ruby
require 'cowsay'
require 'optparse'
options = {}
OptionParser.new do |opts|
opts.banner = "Usage: cowsay [-h] [-f cowfile] [-l] [message]"
opts.on("-l", "List available cow files") do |cowfile|
options['list'] = true
end
opts.on("-f COWFILE", "Specify a cow file") do |cowfile|
options['cowfile'] = cowfile
end
end.parse!
if options['list']
puts "Cow files:"
puts Cowsay.character_classes.join(' ')
else
if ARGV.any?
message = ARGV.join(' ')
else
#retrieve any piped input, otherwise use the empty string.
message = STDIN.tty? ? '' : ARGF.read.chomp
end
puts Cowsay.say(message, options['cowfile'])
end

View File

@@ -0,0 +1,22 @@
# -*- encoding: utf-8 -*-
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'cowsay/version'
Gem::Specification.new do |gem|
gem.name = 'cowsay'
gem.version = Cowsay::VERSION
gem.authors = ['JohnnyT']
gem.email = ['johnnyt@moneydesktop.com']
gem.description = %q{ASCII art avatars emote your messages}
gem.summary = gem.description
gem.homepage = 'https://github.com/moneydesktop/cowsay'
gem.files = `git ls-files`.split($/)
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
gem.require_paths = ['lib']
gem.add_development_dependency 'rake'
# gem.add_development_dependency 'rspec-pride'
end

View File

@@ -0,0 +1,28 @@
require 'cowsay/version'
require 'cowsay/character'
module ::Cowsay
module_function # all instance methods are available on the module (class) level
def random_character
random_class = Character.const_get(character_classes[rand(character_classes.length)])
random_class.new
end
def character_classes
@character_classes ||= Character.constants.map { |c| c.to_sym } - [:Base, :Template]
end
def say(message, character)
character ||= 'cow'
if character == 'random'
random_character.say(message)
else
if character_classes.include? character.capitalize.to_sym
Character.const_get(character.capitalize).say(message)
else
puts "No cow file found for #{character}. Use the -l flag to see a list of available cow files."
end
end
end
end

View File

@@ -0,0 +1,9 @@
module Cowsay
module Character
autoload :Base, 'cowsay/character/base'
end
end
Dir[File.expand_path('character/*.rb', File.dirname(__FILE__))].each do |character|
require character
end

View File

@@ -0,0 +1,73 @@
module Cowsay
module Character
class Base
MAX_LINE_LENGTH = 36 unless defined?(MAX_LINE_LENGTH)
def self.say(message)
new.say(message)
end
def initialize
@thoughts = '\\'
end
def say(message)
render_balloon(message) + render_character
end
def template
raise '#template should be subclassed'
end
private
def render_character
template
end
def render_balloon(message)
message_lines = format_message(message)
line_length = message_lines.max{ |a,b| a.length <=> b.length }.length
output_lines = []
output_lines << " #{'_' * (line_length + 2)} "
message_lines.each do |line|
# 'Here is your message: %s' % 'hello world'
# is the same as
# printf('Here is your message: %s', 'hello world')
output_lines << "| %-#{line_length}s |" % line
end
output_lines << " #{'-' * (line_length + 2)} "
output_lines << ''
output_lines.join("\n")
end
def format_message(message)
return [message] if message.length <= MAX_LINE_LENGTH
lines = []
words = message.split(/\s/).reject{ |word| word.length.zero? }
new_line = ''
words.each do |word|
new_line << "#{word} "
if new_line.length > MAX_LINE_LENGTH
lines << new_line.chomp
new_line = ''
end
end
lines << new_line.chomp unless new_line.length.zero?
lines
end
end
end
end

View File

@@ -0,0 +1,31 @@
module Cowsay
module Character
class Beavis < Base
def template
<<-TEMPLATE
#{@thoughts} __------~~-,
#{@thoughts} ,' ,
/ \\
/ :
| '
| |
| |
| _-- |
_| =-. .-. ||
o|/o/ _. |
/ ~ \\ |
(____\@) ___~ |
|_===~~~.` |
_______.--~ |
\\________ |
\\ |
__/-___-- -__
/ _ \\
TEMPLATE
end
end
end
end

View File

@@ -0,0 +1,17 @@
module Cowsay
module Character
class Bunny < Base
def template
<<-TEMPLATE
#{@thoughts}
#{@thoughts} \\
\\ /\\
( )
.( o ).
TEMPLATE
end
end
end
end

View File

@@ -0,0 +1,30 @@
module Cowsay
module Character
class Cheese < Base
def template
<<-TEMPLATE
#{@thoughts}
#{@thoughts}
_____ _________
/ \\_/ |
| ||
| ||
| ###\\ /### | |
| 0 \\/ 0 | |
/| | |
/ | < |\\ \\
| /| | | |
| | \\_______/ | | |
| | | / /
/|| /|||
----------------|
| | | |
*** ***
/___\\ /___\\
TEMPLATE
end
end
end
end

View File

@@ -0,0 +1,17 @@
module Cowsay
module Character
class Cow < Base
def template
<<-TEMPLATE
#{@thoughts} ^__^
#{@thoughts} (oo)\\_______
(__)\\ )\\/\\
||----w |
|| ||
TEMPLATE
end
end
end
end

View File

@@ -0,0 +1,31 @@
module Cowsay
module Character
class Daemon < Base
def template
<<-TEMPLATE
#{@thoughts} , ,
#{@thoughts} /( )`
#{@thoughts} \\ \\___ / |
/- _ `-/ '
(/\\/ \\ \\ /\\
/ / | ` \\
O O ) / |
`-^--'`< '
(_.) _ ) /
`.___/` /
`-----' /
<----. __ / __ \\
<----|====O)))==) \\) /====
<----' `--' `.__,' \\
| |
\\ /
______( (_ / \\______
,' ,-----' | \\
`--{__________) \\/
TEMPLATE
end
end
end
end

View File

@@ -0,0 +1,27 @@
module Cowsay
module Character
class Dragon < Base
def template
<<-TEMPLATE
#{@thoughts} / \\ //\\
#{@thoughts} |\\___/| / \\// \\\\
/0 0 \\__ / // | \\ \\
/ / \\/_/ // | \\ \\
\@_^_\@'/ \\/_ // | \\ \\
//_^_/ \\/_ // | \\ \\
( //) | \\/// | \\ \\
( / /) _|_ / ) // | \\ _\\
( // /) '/,_ _ _/ ( ; -. | _ _\\.-~ .-~~~^-.
(( / / )) ,-{ _ `-.|.-~-. .~ `.
(( // / )) '/\\ / ~-. _ .-~ .-~^-. \\
(( /// )) `. { } / \\ \\
(( / )) .----~-.\\ \\-' .~ \\ `. \\^-.
///.----..> \\ _ -~ `. ^-` ^-_
///-._ _ _ _ _ _ _}^ - - - - ~ ~-- ,.-~
TEMPLATE
end
end
end
end

View File

@@ -0,0 +1,22 @@
module Cowsay
module Character
class Elephant < Base
def template
<<-TEMPLATE
#{@thoughts} /\\ ___ /\\
#{@thoughts} // \\/ \\/ \\\\
(( O O ))
\\\\ / \\ //
\\/ | | \\/
| | | |
| | | |
| o |
| | | |
|m| |m|
TEMPLATE
end
end
end
end

View File

@@ -0,0 +1,19 @@
module Cowsay
module Character
class Frogs < Base
def template
<<-TEMPLATE
#{@thoughts}
#{@thoughts}
oO)-. .-(Oo
/__ _\\ /_ __\\
\\ \\( | ()~() | )/ /
\\__|\\ | (-___-) | /|__/
' '--' ==`-'== '--' '
TEMPLATE
end
end
end
end

View File

@@ -0,0 +1,33 @@
module Cowsay
module Character
class Ghostbusters < Base
def template
<<-TEMPLATE
#{@thoughts}
#{@thoughts}
#{@thoughts} __---__
_- /--______
__--( / \\ )XXXXXXXXXXX\\v.
.-XXX( O O )XXXXXXXXXXXXXXX-
/XXX( U ) XXXXXXX\\
/XXXXX( )--_ XXXXXXXXXXX\\
/XXXXX/ ( O ) XXXXXX \\XXXXX\\
XXXXX/ / XXXXXX \\__ \\XXXXX
XXXXXX__/ XXXXXX \\__---->
---___ XXX__/ XXXXXX \\__ /
\\- --__/ ___/\\ XXXXXX / ___--/=
\\-\\ ___/ XXXXXX '--- XXXXXX
\\-\\/XXX\\ XXXXXX /XXXXX
\\XXXXXXXXX \\ /XXXXX/
\\XXXXXX > _/XXXXX/
\\XXXXX--__/ __-- XXXX/
-XXXXXXXX--------------- XXXXXX-
\\XXXXXXXXXXXXXXXXXXXXXXXXXX/
""VXXXXXXXXXXXXXXXXXXV""
TEMPLATE
end
end
end
end

View File

@@ -0,0 +1,19 @@
module Cowsay
module Character
class Kitty < Base
def template
<<-TEMPLATE
#{@thoughts}
#{@thoughts}
("`-' '-/") .___..--' ' "`-._
` *_ * ) `-. ( ) .`-.__. `)
(_Y_.) ' ._ ) `._` ; `` -. .-'
_.. `--'_..-_/ /--' _ .' ,4
( i l ),-'' ( l i),' ( ( ! .-'
TEMPLATE
end
end
end
end

View File

@@ -0,0 +1,19 @@
module Cowsay
module Character
class Koala < Base
def template
<<-TEMPLATE
#{@thoughts}
#{@thoughts}
___
{~._.~}
( Y )
()~*~()
(_)-(_)
TEMPLATE
end
end
end
end

View File

@@ -0,0 +1,19 @@
module Cowsay
module Character
class Moose < Base
def template
<<-TEMPLATE
#{@thoughts}
#{@thoughts} \\_\\_ _/_/
#{@thoughts} \\__/
(oo)\\_______
(__)\\ )\\/\\
||----w |
|| ||
TEMPLATE
end
end
end
end

View File

@@ -0,0 +1,29 @@
module Cowsay
module Character
class Ren < Base
def template
<<-TEMPLATE
#{@thoughts}
#{@thoughts}
____
/# /_\\_
| |/o\\o\\
| \\\\_/_/
/ |_ |
| ||\\_ ~|
| ||| \\/
| |||_
\\// |
|| |
||_ \\
\\_| o|
/\\___/
/ ||||__
(___)_)
TEMPLATE
end
end
end
end

View File

@@ -0,0 +1,20 @@
module Cowsay
module Character
class Sheep < Base
def template
<<-TEMPLATE
#{@thoughts}
#{@thoughts}
__
UooU\\.'\@\@\@\@\@\@`.
\\__/(\@\@\@\@\@\@\@\@\@\@)
(\@\@\@\@\@\@\@\@)
`YY~~~~YY'
|| ||
TEMPLATE
end
end
end
end

View File

@@ -0,0 +1,26 @@
module Cowsay
module Character
class Stegosaurus < Base
def template
<<-TEMPLATE
#{@thoughts} . .
#{@thoughts} / `. .' "
#{@thoughts} .---. < > < > .---.
#{@thoughts} | \\ \\ - ~ ~ - / / |
_____ ..-~ ~-..-~
| | \\~~~\\.' `./~~~/
--------- \\__/ \\__/
.' O \\ / / \\ "
(_____, `._.' | } \\/~~~/
`----. / } | / \\__/
`-. | / | / `. ,~~|
~-.__| /_ - ~ ^| /- _ `..-'
| / | / ~-. `-. _ _ _
|_____| |_____| ~ - . _ _ _ _ _>
TEMPLATE
end
end
end
end

View File

@@ -0,0 +1,26 @@
module Cowsay
module Character
class Stimpy < Base
def template
<<-TEMPLATE
#{@thoughts} . _ .
#{@thoughts} |\\_|/__/|
/ / \\/ \\ \\
/__|O||O|__ \\
|/_ \\_/\\_/ _\\ |
| | (____) | ||
\\/\\___/\\__/ //
(_/ ||
| ||
| ||\\
\\ //_/
\\______//
__ || __||
(____(____)
TEMPLATE
end
end
end
end

View File

@@ -0,0 +1,33 @@
module Cowsay
module Character
class Turkey < Base
def template
<<-TEMPLATE
#{@thoughts} ,+*^^*+___+++_
#{@thoughts} ,*^^^^ )
#{@thoughts} _+* ^**+_
#{@thoughts} +^ _ _++*+_+++_, )
_+^^*+_ ( ,+*^ ^ \\+_ )
{ ) ( ,( ,_+--+--, ^) ^\\
{ (\@) } f ,( ,+-^ __*_*_ ^^\\_ ^\\ )
{:;-/ (_+*-+^^^^^+*+*<_ _++_)_ ) ) /
( / ( ( ,___ ^*+_+* ) < < \\
U _/ ) *--< ) ^\\-----++__) ) ) )
( ) _(^)^^)) ) )\\^^^^^))^*+/ / /
( / (_))_^)) ) ) ))^^^^^))^^^)__/ +^^
( ,/ (^))^)) ) ) ))^^^^^^^))^^) _)
*+__+* (_))^) ) ) ))^^^^^^))^^^^^)____*^
\\ \\_)^)_)) ))^^^^^^^^^^))^^^^)
(_ ^\\__^^^^^^^^^^^^))^^^^^^^)
^\\___ ^\\__^^^^^^))^^^^^^^^)\\\\
^^^^^\\uuu/^^\\uuu/^^^^\\^\\^\\^\\^\\^\\^\\^\\
___) >____) >___ ^\\_\\_\\_\\_\\_\\_\\)
^^^//\\\\_^^//\\\\_^ ^(\\_\\_\\_\\)
^^^ ^^ ^^^ ^
TEMPLATE
end
end
end
end

View File

@@ -0,0 +1,27 @@
module Cowsay
module Character
class Turtle < Base
def template
<<-TEMPLATE
#{@thoughts} ___-------___
#{@thoughts} _-~~ ~~-_
#{@thoughts} _-~ /~-_
/^\\__/^\\ /~ \\ / \\
/| O|| O| / \\_______________/ \\
| |___||__| / / \\ \\
| \\ / / \\ \\
| (_______) /______/ \\_________ \\
| / / \\ / \\
\\ \\^\\\\ \\ / \\ /
\\ || \\______________/ _-_ //\\__//
\\ ||------_-~~-_ ------------- \\ --/~ ~\\ || __/
~-----||====/~ |==================| |/~~~~~
(_(__/ ./ / \\_\\ \\.
(_(___/ \\_____)_)
TEMPLATE
end
end
end
end

View File

@@ -0,0 +1,21 @@
module Cowsay
module Character
class Tux < Base
def template
<<-TEMPLATE
#{@thoughts}
#{@thoughts}
.--.
|o_o |
|:_/ |
// \\ \\
(| | )
/'\\_ _/`\\
\\___)=(___/
TEMPLATE
end
end
end
end

View File

@@ -0,0 +1,3 @@
module Cowsay
VERSION = '0.3.0'
end

View File

@@ -0,0 +1,33 @@
# -*- encoding: utf-8 -*-
# stub: cowsay 0.3.0 ruby lib
Gem::Specification.new do |s|
s.name = "cowsay".freeze
s.version = "0.3.0"
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
s.require_paths = ["lib".freeze]
s.authors = ["JohnnyT".freeze]
s.date = "2016-09-29"
s.description = "ASCII art avatars emote your messages".freeze
s.email = ["johnnyt@moneydesktop.com".freeze]
s.executables = ["cowsay".freeze]
s.files = ["bin/cowsay".freeze]
s.homepage = "https://github.com/moneydesktop/cowsay".freeze
s.rubygems_version = "3.0.3".freeze
s.summary = "ASCII art avatars emote your messages".freeze
s.installed_by_version = "3.0.3" if s.respond_to? :installed_by_version
if s.respond_to? :specification_version then
s.specification_version = 4
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
s.add_development_dependency(%q<rake>.freeze, [">= 0"])
else
s.add_dependency(%q<rake>.freeze, [">= 0"])
end
else
s.add_dependency(%q<rake>.freeze, [">= 0"])
end
end

View File

@@ -0,0 +1,5 @@
# frozen_string_literal: true
source "https://rubygems.org"
gem "cowsay", "~> 0.3.0"

View File

@@ -0,0 +1,13 @@
GEM
remote: https://rubygems.org/
specs:
cowsay (0.3.0)
PLATFORMS
ruby
DEPENDENCIES
cowsay (~> 0.3.0)
BUNDLED WITH
2.0.1

View File

@@ -0,0 +1,7 @@
require 'cowsay'
Handler = Proc.new do |req, res|
res.status = 200
res['Content-Type'] = 'text/plain'
res.body = Cowsay.say('gem:RANDOMNESS_PLACEHOLDER', 'cow')
end

View File

@@ -0,0 +1,5 @@
{
"version": 2,
"builds": [{ "src": "index.rb", "use": "@now/ruby" }],
"probes": [{ "path": "/", "mustContain": "gem:RANDOMNESS_PLACEHOLDER" }]
}

View File

@@ -0,0 +1,7 @@
{
"version": 2,
"builds": [{ "src": "project/index.rb", "use": "@now/ruby" }],
"probes": [
{ "path": "/project/", "mustContain": "gem:RANDOMNESS_PLACEHOLDER" }
]
}

View File

@@ -0,0 +1,10 @@
require 'webrick'
require 'cowsay'
class Handler < WEBrick::HTTPServlet::AbstractServlet
def do_GET req, res
res.status = 200
res['Content-Type'] = 'text/plain'
res.body = Cowsay.say('gem:RANDOMNESS_PLACEHOLDER', 'cow')
end
end

View File

@@ -0,0 +1,27 @@
#!/usr/bin/env ruby
#
# This file was generated by RubyGems.
#
# The application 'cowsay' is installed as part of a gem, and
# this file is here to facilitate running it.
#
require 'rubygems'
version = ">= 0.a"
str = ARGV.first
if str
str = str.b[/\A_(.*)_\z/, 1]
if str and Gem::Version.correct?(str)
version = str
ARGV.shift
end
end
if Gem.respond_to?(:activate_bin_path)
load Gem.activate_bin_path('cowsay', 'cowsay', version)
else
gem "cowsay", version
load Gem.bin_path("cowsay", "cowsay", version)
end

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