diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d163863 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build/ \ No newline at end of file diff --git a/build_apps.d b/build_apps.d new file mode 100755 index 0000000..db1b9df --- /dev/null +++ b/build_apps.d @@ -0,0 +1,150 @@ +#!/usr/bin/env rdmd +/** + * A simple build script that builds all Gymboard applications and collects + * build artifacts for deployment. You can run it with `./build_apps.d`, and it + * will build each application for production deployment using a separate + * thread. + * + * Supply the "clean" command to remove all build artifacts instead of building. + */ +module build_apps; + +import std.stdio; +import std.process; +import std.file; +import std.path; +import std.regex; +import core.thread; + +enum BUILDS_DIR = "build"; + +int main(string[] args) { + if (args.length > 1 && args[1] == "clean") { + rmdirRecurse(BUILDS_DIR); + return 0; + } + Thread[] buildThreads = [ + runBuildInThread(API_BUILD_SPEC), + runBuildInThread(CDN_BUILD_SPEC), + runBuildInThread(SEARCH_BUILD_SPEC), + runBuildInThread(APP_BUILD_SPEC), + runBuildInThread(UPLOADS_BUILD_SPEC) + ]; + + foreach (t; buildThreads) t.join(); + + return 0; +} + +/** + * Specification for building an application. + */ +struct BuildSpec { + string name; + string workingDir; + string buildCommand; + typeof(ctRegex!(`\d`)) artifactRegex; +} + +static immutable API_BUILD_SPEC = BuildSpec( + "api", + "gymboard-api", + "./mvnw clean package spring-boot:repackage", + ctRegex!(`target/gymboard-api-.*\.jar$`) +); + +static immutable CDN_BUILD_SPEC = BuildSpec( + "cdn", + "gymboard-cdn", + "./mvnw clean package spring-boot:repackage", + ctRegex!(`target/gymboard-cdn-.*\.jar$`) +); + +static immutable SEARCH_BUILD_SPEC = BuildSpec( + "search", + "gymboard-search", + "./mvnw clean package spring-boot:repackage -DskipTests=true", + ctRegex!(`target/gymboard-search-.*\.jar$`) +); + +static immutable APP_BUILD_SPEC = BuildSpec( + "app", + "gymboard-app", + "quasar build", + ctRegex!(`gymboard-app/dist$`) +); + +static immutable UPLOADS_BUILD_SPEC = BuildSpec( + "uploads", + "gymboard-uploads", + "dub build --build=release", + ctRegex!(`gymboard-uploads/gymboard-uploads$`) +); + +/** + * Runs a build and returns its exit code. + * Params: + * spec = The build spec to run. + * Returns: The exit code of the build. 0 indicates success. + */ +int runBuild(const BuildSpec spec) { + string buildDir = buildPath(BUILDS_DIR, spec.name); + if (exists(buildDir)) rmdirRecurse(buildDir); + mkdirRecurse(buildDir); + File buildLogFile = File(buildPath(buildDir, "build.log"), "w"); + File buildErrorLogFile = File(buildPath(buildDir, "build-error.log"), "w"); + Pid pid = spawnShell( + spec.buildCommand, + std.stdio.stdin, + buildLogFile, + buildErrorLogFile, + null, + Config.none, + spec.workingDir, + nativeShell() + ); + int result = wait(pid); + if (result != 0) return result; + + // Find and extract artifacts. + bool artifactFound = false; + foreach (DirEntry entry; dirEntries(spec.workingDir, SpanMode.breadth, false)) { + Captures!string c = matchFirst(entry.name, spec.artifactRegex); + if (!c.empty) { + artifactFound = true; + string destFilename = buildPath(buildDir, baseName(entry.name)); + writefln!"Copying artifact %s to %s"(entry.name, destFilename); + if (entry.isFile) { + writefln!" Filesize: %d"(entry.size); + copy(entry.name, destFilename); + } else if (entry.isDir) { + Pid cpPid = spawnShell("cp -R " ~ entry.name ~ " " ~ destFilename); + int cpResult = wait(cpPid); + if (cpResult != 0) { + writefln!"Failed to copy directory: %d"(cpResult); + return cpResult; + } + } + } + } + if (!artifactFound) { + writefln!"Warning: Build %s completed successfully, but no matching artifacts were found."(spec.name); + } + return 0; +} + +/** + * Wraps building of a build spec in a thread. + * Params: + * spec = The spec to build. + * Returns: The thread running the build, which is already started. + */ +Thread runBuildInThread(const BuildSpec spec) { + Thread t = new Thread(() { + writefln!"Building \"%s\" in %s"(spec.name, spec.workingDir); + int result = runBuild(spec); + writefln!"Build \"%s\" exited with code %d."(spec.name, result); + }); + t.start(); + return t; +} diff --git a/gymboard-uploads/README.md b/gymboard-uploads/README.md index 18a9670..d84dada 100644 --- a/gymboard-uploads/README.md +++ b/gymboard-uploads/README.md @@ -18,3 +18,5 @@ Therefore, **gymboard-uploads** is responsible for the following actions: - Processing of raw videos to reduce file size and optimize for web. This will most likely be done using FFMPEG in the backend. - Reporting the status of video processing for recently uploaded videos. - Serving processed video files. + +**THIS PROJECT IS A WORK-IN-PROGRESS** Do not use it for production.