Skip to content

Commit e0d35e2

Browse files
Taytaylovasoa
andauthored
Create standardized Docker development environment via .devcontainer (sql-js#445)
* Initial default .devcontainer and Dockerfile created * Install EMSDK and Chrome dependencies in the Dockerfile * Move Contributing/Compiling instructions to Contributing.md * Start worker test in Puppeteer without Chrome sandbox when running in Docker * Add `clean` package script and express `rebuild` script in terms of npm scripts This aims to express all build tasks via npm scripts via make commands, rather than expressing some scripts as npm scripts and others in terms of make. * Pin EMSDK to 2.0.15 * First attempt at Github action building inside of Docker container * Switch cmd to entrypoint * Create an entrypoint.sh for the Github build-sqljs action * remove comments * Make action script executable * Set correct path in docker image Co-authored-by: lovasoa <[email protected]>
1 parent baad65c commit e0d35e2

File tree

10 files changed

+269
-31
lines changed

10 files changed

+269
-31
lines changed

.devcontainer/Dockerfile

+127
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# We build our DevContainer on MS' Typescript-Node Devcontainer
2+
# This gives us lots of standard stuff, and lets us layer a few custom things on top, like the Emscripten compiler, Puppeteer
3+
4+
# --------------------------------------------------------------------
5+
# BEGIN Standard MS Devcontainer for Typescript-Node
6+
7+
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.155.1/containers/typescript-node/.devcontainer/base.Dockerfile
8+
# [Choice] Node.js version: 14, 12, 10
9+
ARG VARIANT="14-buster"
10+
FROM mcr.microsoft.com/vscode/devcontainers/typescript-node:0-${VARIANT}
11+
12+
# [Optional] Uncomment if you want to install an additional version of node using nvm
13+
# ARG EXTRA_NODE_VERSION=10
14+
# RUN su node -c "source /usr/local/share/nvm/nvm.sh && nvm install ${EXTRA_NODE_VERSION}"
15+
16+
# [Optional] Uncomment if you want to install more global node packages
17+
# RUN su node -c "npm install -g <your-package-list -here>"
18+
19+
# END Standard MS Devcontainer for Typescript-Node
20+
# --------------------------------------------------------------------
21+
22+
# --------------------------------------------------------------------
23+
# BEGIN EMSDK
24+
# Install EMSDK to /emsdk just like the EMSDK Dockerfile: https://github.com/emscripten-core/emsdk/blob/master/docker/Dockerfile
25+
ENV EMSDK /emsdk
26+
# We pin EMSDK to 2.0.15 rather than 'latest' so that everyone is using the same compiler version
27+
ENV EMSCRIPTEN_VERSION 2.0.15
28+
29+
RUN git clone https://github.com/emscripten-core/emsdk.git $EMSDK
30+
31+
RUN echo "## Install Emscripten" \
32+
&& cd ${EMSDK} \
33+
&& ./emsdk install ${EMSCRIPTEN_VERSION} \
34+
&& echo "## Done"
35+
36+
# Copied directly from https://github.com/emscripten-core/emsdk/blob/master/docker/Dockerfile
37+
RUN cd ${EMSDK} \
38+
&& echo "## Generate standard configuration" \
39+
&& ./emsdk activate $EMSCRIPTEN_VERSION \
40+
&& chmod 777 ${EMSDK}/upstream/emscripten \
41+
&& chmod -R 777 ${EMSDK}/upstream/emscripten/cache \
42+
&& echo "int main() { return 0; }" > hello.c \
43+
&& ${EMSDK}/upstream/emscripten/emcc -c hello.c \
44+
&& cat ${EMSDK}/upstream/emscripten/cache/sanity.txt \
45+
&& echo "## Done"
46+
47+
ENV PATH $EMSDK:$EMSDK/upstream/emscripten/:$PATH
48+
49+
# Cleanup Emscripten installation and strip some symbols
50+
RUN echo "## Aggressive optimization: Remove debug symbols" \
51+
&& cd ${EMSDK} && . ./emsdk_env.sh \
52+
# Remove debugging symbols from embedded node (extra 7MB)
53+
&& strip -s `which node` \
54+
# Tests consume ~80MB disc space
55+
&& rm -fr ${EMSDK}/upstream/emscripten/tests \
56+
# Fastcomp is not supported
57+
&& rm -fr ${EMSDK}/upstream/fastcomp \
58+
# strip out symbols from clang (~extra 50MB disc space)
59+
&& find ${EMSDK}/upstream/bin -type f -exec strip -s {} + || true \
60+
&& echo "## Done"
61+
62+
RUN echo ". /emsdk/emsdk_env.sh" >> /etc/bash.bashrc
63+
# END EMSDK
64+
# --------------------------------------------------------------------
65+
66+
# --------------------------------------------------------------------
67+
# BEGIN PUPPETEER dependencies
68+
# Here we install all of the packages depended upon by Chrome (that Puppeteer will use for headless tests).
69+
# We could also take a page from https://github.com/buildkite/docker-puppeteer/blob/master/Dockerfile instead,
70+
# and install the latest stable version of Chrome to get the right dependencies, but that version changes over time,
71+
# so the stable version of Chrome and the version installed by Puppeteer might diverge over time.
72+
# It also means they end up having Chrome downloaded and installed twice.
73+
# We could install the particular version of Chrome that our version of Puppeteer would use and then tell Puppeteer not to download its own version of Chrome,
74+
# but then we'd have to rebuild our Docker container every time we revved Puppeteer, and that feels fiddly too.
75+
# For all of these reasons, it seems safer to simply install the explicit list packages depended upon by Chrome, assume that's unlikely to change
76+
# and move on.
77+
78+
# List taken from:
79+
# https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md#chrome-headless-doesnt-launch-on-unix
80+
RUN apt-get update \
81+
&& apt-get install -y \
82+
ca-certificates \
83+
fonts-liberation \
84+
libappindicator3-1 \
85+
libasound2 \
86+
libatk-bridge2.0-0 \
87+
libatk1.0-0 \
88+
libc6 \
89+
libcairo2 \
90+
libcups2 \
91+
libdbus-1-3 \
92+
libexpat1 \
93+
libfontconfig1 \
94+
libgbm1 \
95+
libgcc1 \
96+
libglib2.0-0 \
97+
libgtk-3-0 \
98+
libnspr4 \
99+
libnss3 \
100+
libpango-1.0-0 \
101+
libpangocairo-1.0-0 \
102+
libstdc++6 \
103+
libx11-6 \
104+
libx11-xcb1 \
105+
libxcb1 \
106+
libxcomposite1 \
107+
libxcursor1 \
108+
libxdamage1 \
109+
libxext6 \
110+
libxfixes3 \
111+
libxi6 \
112+
libxrandr2 \
113+
libxrender1 \
114+
libxss1 \
115+
libxtst6 \
116+
lsb-release \
117+
wget \
118+
xdg-utils
119+
120+
# We set this env variable (RUN_WORKER_TEST_WITHOUT_PUPPETEER_SANDBOX=1) this to tell our sql.js test harness to run Puppeteer without the sandbox.
121+
# Otherwise, when we instantiate Puppeteer, we get this error:
122+
# Puppeteer can't start due to a sandbox error. (Details follow.)
123+
# [0321/173044.694524:FATAL:zygote_host_impl_linux.cc(117)] No usable sandbox! Update your kernel or see https://chromium.googlesource.com/chromium/src/+/master/docs/linux/suid_sandbox_development.md for more information on developing with the SUID sandbox. If you want to live dangerously and need an immediate workaround, you can try using --no-sandbox.
124+
ENV RUN_WORKER_TEST_WITHOUT_PUPPETEER_SANDBOX=1
125+
126+
# END PUPPETEER
127+
# --------------------------------------------------------------------

.devcontainer/devcontainer.json

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
2+
// https://github.com/microsoft/vscode-dev-containers/tree/v0.155.1/containers/typescript-node
3+
{
4+
"name": "Node.js & TypeScript",
5+
"build": {
6+
"dockerfile": "Dockerfile",
7+
// Update 'VARIANT' to pick a Node version: 10, 12, 14
8+
"args": {
9+
"VARIANT": "14"
10+
},
11+
},
12+
// Set *default* container specific settings.json values on container create.
13+
"settings": {},
14+
// Add the IDs of extensions you want installed when the container is created.
15+
"extensions": [
16+
"dbaeumer.vscode-eslint"
17+
],
18+
// Use 'forwardPorts' to make a list of ports inside the container available locally.
19+
// "forwardPorts": [],
20+
// Use 'postCreateCommand' to run commands after the container is created.
21+
// "postCreateCommand": "yarn install",
22+
// Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
23+
"remoteUser": "node"
24+
}
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# action.yml
2+
name: 'Build SQL.js'
3+
description: 'Builds sql.js using the .devcontainer/Dockerfile as its environment'
4+
runs:
5+
using: 'docker'
6+
image: '../../../.devcontainer/Dockerfile'
7+
entrypoint: "/github/workspace/.github/actions/build-sqljs/entrypoint.sh"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/bin/bash
2+
3+
set -e
4+
5+
cd /github/workspace/
6+
npm run rebuild

.github/workflows/CI.yml

+1-10
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,8 @@ jobs:
77
runs-on: ubuntu-latest
88
steps:
99
- uses: actions/checkout@v2
10-
- uses: actions/cache@v1
11-
id: cache
12-
with:
13-
path: '.emsdk-cache'
14-
key: emscripten-2.0.6
15-
- uses: mymindstorm/setup-emsdk@ca33dc66a6b178f65393989c12e9465baf053352
16-
with:
17-
version: '2.0.6'
18-
actions-cache-folder: '.emsdk-cache'
1910
- name: make
20-
run: make
11+
uses: ./.github/actions/build-sqljs
2112
- uses: actions/upload-artifact@v2
2213
with: {name: dist, path: dist}
2314
- name: test

CONTRIBUTING.md

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
2+
# Compiling and Contributing
3+
4+
General consumers of this library don't need to read any further. (The compiled files are available via the [release page](https://github.com/sql-js/sql.js/releases).)
5+
6+
If you want to compile your own version of SQLite for WebAssembly, or want to contribute to this project, read on.
7+
8+
## Setting up your Development Environment
9+
10+
### Containerized Development Environment (Recommended)
11+
12+
This project defines a standardized development environment using Docker (and the .devcontainer spec in particular). This allows for anyone on any platform to get up and running quickly. (VSCode is not technically required to make use of this standardized environment, but it makes containerized development so seamless that the non-VSCode path is not currently documented here.)
13+
14+
Standardizing our development environment has numerous benefits:
15+
- Allows anyone on ANY platform (Linux, Mac, and Windows) to contribute or compile their own build.
16+
- It's quicker and easier for any contributor to dive in and fix issues.
17+
- (Practically) eliminates configuration bugs that are difficult for maintainers to reproduce. Also known as "works on my machine" issues.
18+
- Allows us to write our scripts assuming that they're _always_ running in a single known environment of a single, known platform.
19+
- Ensure that all contributors use a known, standardized installation of EMSDK.
20+
- Allows for a more clearly documented process for updating the EMSDK to a new version.
21+
- End-Users that simply want to compile and install their own version of SQLite don't have to bother with EMSDK installation in their particular environment.
22+
23+
To get started:
24+
25+
1. Follow the [Installation Steps for Containerized Development in VSCode](https://code.visualstudio.com/docs/remote/containers#_installation). This includes installing Docker, VSCode, and the [Remote Development extension pack](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack) for VSCode)
26+
2. Clone this repository
27+
3. Open the repository folder in VSCode. It will detect the presence of a .devcontainer and prompt you: "Folder contains a Dev Container configuration file. Reopen folder to develop in a container." Click "Reopen in container"
28+
29+
You're now ready to test the dev environment:
30+
4. Click on Terminal->New Terminal to be dropped into a terminal inside the dev environment.
31+
5. Run `$ npm install` to install the required modules
32+
6. Run `$ npm test` to ensure all tests pass
33+
7. Run `$ npm rebuild` to re-compile the project from scratch (using the version of EMSDK installed in the container).
34+
8. Run `$ npm test` to ensure all tests pass after said rebuild
35+
36+
You're now ready for development!
37+
38+
### Host-based configuration (Not recommended)
39+
40+
If you're on a Mac or Linux-based host machine, you can install and use the EMSDK directly to perform a build.
41+
Note that if you run into bugs with this configuration, we highly encourage you to use the containerized development environment instead, as detailed above.
42+
43+
Instructions:
44+
45+
1. [Install the EMSDK](https://emscripten.org/docs/getting_started/downloads.html)
46+
2. Clone this repository
47+
3. Run `$ npm install` to install the required modules
48+
4. Run `$ npm test` to ensure all tests pass
49+
5. Run `$ npm rebuild` to re-compile the project from scratch (using the version of EMSDK installed in the container).
50+
6. Run `$ npm test` to ensure all tests pass after said rebuild
51+
52+
## Compiling SQLite with different options
53+
54+
In order to enable extensions like JSON1 or FTS5, change the CFLAGS in the [Makefile](Makefile) and run `npm run rebuild`:
55+
56+
``` diff
57+
CFLAGS = \
58+
-O2 \
59+
-DSQLITE_OMIT_LOAD_EXTENSION \
60+
-DSQLITE_DISABLE_LFS \
61+
-DSQLITE_ENABLE_FTS3 \
62+
-DSQLITE_ENABLE_FTS3_PARENTHESIS \
63+
+ -DSQLITE_ENABLE_FTS5 \
64+
+ -DSQLITE_ENABLE_JSON1 \
65+
-DSQLITE_THREADSAFE=0
66+
```

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Note: Last built with version 1.38.30 of Emscripten
1+
# Note: Last built with version 2.0.15 of Emscripten
22

33
# TODO: Emit a file showing which version of emcc and SQLite was used to compile the emitted output.
44
# TODO: Create a release on Github with these compiled assets rather than checking them in

README.md

+5-18
Original file line numberDiff line numberDiff line change
@@ -322,21 +322,8 @@ For each [release](https://github.com/sql-js/sql.js/releases/), you will find a
322322
- `sql-asm-debug.js` : The _Debug_ asm.js version of Sql.js. Use this for local development.
323323
- `worker.*` - Web Worker versions of the above libraries. More limited API. See [examples/GUI/gui.js](examples/GUI/gui.js) for a good example of this.
324324

325-
## Compiling
326-
327-
- Install the EMSDK, [as described here](https://emscripten.org/docs/getting_started/downloads.html)
328-
- Run `npm run rebuild`
329-
330-
In order to enable extensions like FTS5, change the CFLAGS in the [Makefile](Makefile) and rebuild:
331-
332-
``` diff
333-
CFLAGS = \
334-
-O2 \
335-
-DSQLITE_OMIT_LOAD_EXTENSION \
336-
-DSQLITE_DISABLE_LFS \
337-
-DSQLITE_ENABLE_FTS3 \
338-
-DSQLITE_ENABLE_FTS3_PARENTHESIS \
339-
+ -DSQLITE_ENABLE_FTS5 \
340-
-DSQLITE_ENABLE_JSON1 \
341-
-DSQLITE_THREADSAFE=0
342-
```
325+
## Compiling/Contributing
326+
327+
General consumers of this library don't need to read any further. (The compiled files are available via the [release page](https://github.com/sql-js/sql.js/releases).)
328+
329+
If you want to compile your own version of SQLite for WebAssembly, or want to contribute to this project, see [CONTRIBUTING.md](docs/CONTRIBUTING.md).

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
"main": "./dist/sql-wasm.js",
2121
"scripts": {
2222
"build": "make",
23-
"rebuild": "make clean && make",
23+
"rebuild": "npm run clean && npm run build",
24+
"clean": "make clean",
2425
"test": "npm run lint && npm run test-asm && npm run test-asm-debug && npm run test-wasm && npm run test-wasm-debug && npm run test-asm-memory-growth",
2526
"lint": "eslint .",
2627
"prettify": "eslint . --fix",

test/test_worker.js

+30-1
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@
66
var puppeteer = require("puppeteer");
77
var path = require("path");
88
var fs = require("fs");
9+
const { env } = require("process");
910

1011
class Worker {
1112
constructor(handle) {
1213
this.handle = handle;
1314
}
1415
static async fromFile(file) {
15-
const browser = await puppeteer.launch();
16+
const browser = await Worker.launchBrowser();
1617
const page = await browser.newPage();
1718
const source = fs.readFileSync(file, 'utf8');
1819
const worker = await page.evaluateHandle(x => {
@@ -21,6 +22,34 @@ class Worker {
2122
}, source);
2223
return new Worker(worker);
2324
}
25+
26+
static async launchBrowser(){
27+
try{
28+
return await puppeteer.launch();
29+
}
30+
catch(e){
31+
if (e.stack.includes('No usable sandbox!')){
32+
// It's possible that this exception is n expected error related to not having the ability to create a sandboxed user for Puppeteer in Docker.
33+
// One way around this is to set up the Dockerfile to have a sandboxed user.
34+
// Details on getting Puppeteer running sandboxed while in Docker are here:
35+
// https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md#running-puppeteer-in-docker
36+
// That seemed kinda complicated, so I'm working around it more quickly/straightforwardly by looking for an env variable we set in the Docker fil `RUN_WORKER_TEST_WITHOUT_PUPPETEER_SANDBOX`.
37+
// -- Taytay
38+
if (env['RUN_WORKER_TEST_WITHOUT_PUPPETEER_SANDBOX']=="1"){
39+
// This tells puppeteer to launch without worrying about the sandbox.
40+
// That's not "safe" if you don't trust the code you're loading in the browser,
41+
// but we're in a container and we know what we're testing.
42+
return await puppeteer.launch({args: ['--no-sandbox', '--disable-setuid-sandbox']});
43+
}
44+
else {
45+
console.warn("Puppeteer can't start due to a sandbox error. (Details follow.)\nFor a quick, but potentially dangerous workaround, you can set the environment variable 'RUN_WORKER_TEST_WITHOUT_PUPETEER_SANDBOX=1'.\nYou can also simply run this test in the Docker container defined in .devcontainer/Dockerfile.");
46+
}
47+
}
48+
// If we're here, we couldn't get out of this cleanly. Re-throw
49+
throw e;
50+
}
51+
}
52+
2453
async postMessage(msg) {
2554
return await this.handle.evaluate((worker, msg) => {
2655
return new Promise((accept, reject) => {

0 commit comments

Comments
 (0)