diff --git a/Makefile b/Makefile index 3006357..1cba48f 100644 --- a/Makefile +++ b/Makefile @@ -8,9 +8,10 @@ export BUILD := build/$(T)/$(L) DEB := riju-$(T)-$(L).deb -S3_DEBS := s3://$(S3_BUCKET) -S3_DEB := $(S3_DEBS)/debs/$(DEB) -S3_HASH := $(S3_DEBS)/hashes/riju-$(T)-$(L) +S3 := s3://$(S3_BUCKET) +S3_DEB := $(S3)/debs/$(DEB) +S3_HASH := $(S3)/hashes/riju-$(T)-$(L) +S3_CONFIG := $(S3)/config.json ifneq ($(CMD),) BASH_CMD := bash -c '$(CMD)' @@ -165,7 +166,7 @@ dev: # Compile, run, and watch all artifacts and server for development ## are provided, then only tests matching both are run. test: # [L=[,...]] [T=[,...]] : Run test(s) for language or test category - node backend/test-runner.js $(L) + node backend/test-runner.js ## Functions such as 'repl', 'run', 'format', etc. are available in ## the sandbox, and initial setup has already been done (e.g. 'setup' @@ -185,7 +186,7 @@ lsp: # L= : Run LSP REPL for language or custom command line ### Fetch artifacts from registries -pull: # I= : Pull last published Riju image from Docker Hub +pull: # I= : Pull last published Riju image from Docker registry @: $${I} $${DOCKER_REPO} docker pull $(DOCKER_REPO):$(I) docker tag $(DOCKER_REPO):$(I) riju:$(I) @@ -193,11 +194,15 @@ pull: # I= : Pull last published Riju image from Docker Hub download: # L= T= : Download last published .deb from S3 @: $${L} $${T} $${S3_BUCKET} mkdir -p $(BUILD) - aws s3 cp $(S3_DEB) $(BUILD)/$(DEB) --no-sign-request + aws s3 cp $(S3_DEB) $(BUILD)/$(DEB) + +undeploy: # Pull latest deployment config from S3 + mkdir -p $(BUILD) + aws s3 cp $(S3_CONFIG) $(BUILD)/config.json ### Publish artifacts to registries -push: # I= : Push Riju image to Docker Hub +push: # I= : Push Riju image to Docker registry @: $${I} $${DOCKER_REPO} docker tag riju:$(I) $(DOCKER_REPO):$(I) docker push $(DOCKER_REPO):$(I) @@ -208,6 +213,12 @@ upload: # L= T= : Upload .deb to S3 aws s3 cp $(BUILD)/$(DEB) $(S3_DEB) hash="$$(dpkg-deb -f $(BUILD)/$(DEB) Riju-Script-Hash | grep .)"; aws s3 cp - "$(S3_HASH)/$${hash}" < /dev/null +config: # Generate deployment config file + node tools/generate-deploy-config.js + +deploy: # Upload deployment config to S3 + aws s3 cp $(BUILD)/config.json $(S3_CONFIG) + ### Miscellaneous ## Run this every time you update .gitignore or .dockerignore.in. diff --git a/backend/api.js b/backend/api.js index 6030037..d722589 100644 --- a/backend/api.js +++ b/backend/api.js @@ -1,5 +1,6 @@ import { spawn } from "child_process"; import path from "path"; +import process from "process"; import WebSocket from "ws"; import pty from "node-pty"; @@ -299,11 +300,7 @@ export class Session { template, } = this.config; if (this.term) { - const pid = this.term.pty.pid; - const args = this.privilegedExec( - `kill -SIGTERM ${pid}; sleep 1; kill -SIGKILL ${pid}` - ); - spawn(args[0], args.slice(1)); + process.kill(this.term.pty.pid); // Signal to terminalOutput message generator using closure. this.term.live = false; this.term = null; diff --git a/backend/test-runner.js b/backend/test-runner.js index b7b60b3..e232a23 100644 --- a/backend/test-runner.js +++ b/backend/test-runner.js @@ -623,7 +623,6 @@ async function writeLog(lang, type, result, log) { async function main() { langs = await langsPromise; let tests = getTestList(); - const args = process.argv.slice(2); if (process.env.L) { tests = tests.filter(({ lang }) => process.env.L.split().includes(lang)); } diff --git a/langs/ioke.yaml b/langs/ioke.yaml index 2cf5ef7..709b00d 100644 --- a/langs/ioke.yaml +++ b/langs/ioke.yaml @@ -4,6 +4,9 @@ aliases: name: "Ioke" install: + prepare: + cert: + - "https://letsencrypt.org/certs/lets-encrypt-r3.pem" apt: - default-jdk manual: | diff --git a/langs/mariadb.yaml b/langs/mariadb.yaml index 17b21e0..d2faee7 100644 --- a/langs/mariadb.yaml +++ b/langs/mariadb.yaml @@ -10,11 +10,13 @@ install: - mysql-client riju: - sqls + # MariaDB has Debian package downloads, but only for LTS versions of + # Ubuntu, so we have to download the release tarball instead. manual: | install -d "${pkg}/opt/mariadb" ver="$(curl -sSL https://downloads.mariadb.org/ | grep 'href="/mariadb/[0-9]' | grep -Eo '[0-9][^/]+' | sort -rV | head -n1)" - wget "https://downloads.mariadb.org/f/mariadb-${ver}/bintar-linux-x86_64/mariadb-${ver}-linux-x86_64.tar.gz/from/http%3A//sfo1.mirrors.digitalocean.com/mariadb/?serve" -O mariadb.tar.gz + wget "https://downloads.mariadb.org/f/mariadb-${ver}/bintar-linux-systemd-x86_64/mariadb-${ver}-linux-systemd-x86_64.tar.gz/from/http%3A//sfo1.mirrors.digitalocean.com/mariadb/?serve" -O mariadb.tar.gz tar -xf mariadb.tar.gz -C "${pkg}/opt/mariadb" --strip-components=1 chmod a=rx,u=rwx "${pkg}/opt/mariadb/lib/plugin/auth_pam_tool_dir" chmod a=rx,u=rwxs "${pkg}/opt/mariadb/lib/plugin/auth_pam_tool_dir/auth_pam_tool" diff --git a/langs/mongodb.yaml b/langs/mongodb.yaml index 2e85e92..d7a86de 100644 --- a/langs/mongodb.yaml +++ b/langs/mongodb.yaml @@ -6,10 +6,14 @@ aliases: name: "MongoDB" install: + # The MongoDB package is only available for LTS releases of Ubuntu, + # so we grab it from focal. prepare: aptRepo: - "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ focal main universe" manual: | + sudo --preserve-env=DEBIAN_FRONTEND apt-get update + for name in mongodb mongodb-clients mongodb-server mongodb-server-core; do apt-get download "${name}" mv "${name}"_*.deb "${name}.deb" @@ -42,8 +46,8 @@ template: | print("Hello, world!") run: | - set -e while ps -u "$(id -un)" -o comm | grep -q mongod; do + ps -u "$(id -un)" -o pid,comm | cat sleep 0.01 done rm -rf data diff --git a/tf/infra.tf b/tf/infra.tf index 49e97ba..b53c8d8 100644 --- a/tf/infra.tf +++ b/tf/infra.tf @@ -121,6 +121,10 @@ resource "aws_acm_certificate" "riju" { domain_name = "riju.codes" subject_alternative_names = ["*.riju.codes"] validation_method = "DNS" + + tags = { + Name = "Riju server" + } } resource "aws_s3_bucket" "riju" { diff --git a/tools/generate-build-script.js b/tools/generate-build-script.js index 4831547..0f9fd08 100644 --- a/tools/generate-build-script.js +++ b/tools/generate-build-script.js @@ -25,26 +25,6 @@ function makeLangScript(langConfig, isShared) { const dependsCfg = (install && install.depends) || {}; let prefaceNeedsAptGetUpdate = false; let prepareNeedsAptGetUpdate = false; - if ( - install && - ((install.prepare && - ((install.prepare.manual && - install.prepare.manual.includes("apt-get") && - install.prepare.manual.includes(":i386")) || - (install.prepare.apt && - install.prepare.apt.filter((pkg) => pkg.includes(":i386")).length > - 0))) || - (install.preface && - ((install.preface.manual && - install.preface.manual.includes("apt-get") && - install.preface.manual.includes(":i386")) || - (install.preface.apt && - install.preface.apt.filter((pkg) => pkg.includes(":i386")).length > - 0)))) - ) { - prefaceParts.push(`\ -dpkg --add-architecture i386`); - } if (install) { const { prepare, @@ -302,6 +282,26 @@ chmod +x "${path}"`); if (prefaceNeedsAptGetUpdate) { prefaceParts.unshift(`\ sudo --preserve-env=DEBIAN_FRONTEND apt-get update`); + } + if ( + install && + ((install.prepare && + ((install.prepare.manual && + install.prepare.manual.includes("apt-get") && + install.prepare.manual.includes(":i386")) || + (install.prepare.apt && + install.prepare.apt.filter((pkg) => pkg.includes(":i386")).length > + 0))) || + (install.preface && + ((install.preface.manual && + install.preface.manual.includes("apt-get") && + install.preface.manual.includes(":i386")) || + (install.preface.apt && + install.preface.apt.filter((pkg) => pkg.includes(":i386")).length > + 0)))) + ) { + prefaceParts.unshift(`\ +sudo dpkg --add-architecture i386`); } if (prepareNeedsAptGetUpdate) { parts.unshift(`\ @@ -375,7 +375,7 @@ function makeInstallScript(langConfig) { const { apt, cert, aptKey, aptRepo } = install; if (apt && apt.filter((pkg) => pkg.includes(":i386")).length > 0) { parts.push(`\ -dpkg --add-architecture i386`); +sudo dpkg --add-architecture i386`); } if (cert && cert.length > 0) { parts.push( diff --git a/tools/generate-deploy-config.js b/tools/generate-deploy-config.js new file mode 100644 index 0000000..b3c6d25 --- /dev/null +++ b/tools/generate-deploy-config.js @@ -0,0 +1,24 @@ +import { promises as fs } from "fs"; +import url from "url"; + +// Get the contents of the JSON file that will be written to S3 in +// order to deploy Riju. +async function getDeployConfig() { + // TODO +} + +// Parse command-line arguments, run main functionality, and exit. +async function main() { + const program = new Command(); + program.parse(process.argv); + await fs.mkdir("build", { recursive: true }); + await fs.writeFile("build/config.json", JSON.stringify(await getDeployConfig(), null, 2) + "\n"); + process.exit(0); +} + +if (process.argv[1] === url.fileURLToPath(import.meta.url)) { + main().catch((err) => { + console.error(err); + process.exit(1); + }); +}