151 lines
4.2 KiB
D
151 lines
4.2 KiB
D
|
#!/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;
|
||
|
}
|