All checks were successful
Build, test and publish / verify (push) Successful in 6m34s
108 lines
5.6 KiB
Java
108 lines
5.6 KiB
Java
import java.net.URI;
|
|
import java.nio.file.Files;
|
|
import java.nio.file.Path;
|
|
import java.time.Duration;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
|
|
/** One-time provisioning; secrets stay in owner-only files or Gitea secret storage. */
|
|
class ReleaseSetup {
|
|
static void configure(Path root, Map<String, String> input) throws Exception {
|
|
var env = new HashMap<>(input);
|
|
env.putIfAbsent("GITEA_SERVER_URL", "http://192.168.2.28:3000");
|
|
env.putIfAbsent("CI_REPOSITORY", "wanjakrah/agiledancers");
|
|
env.putIfAbsent("DEPLOY_HOST", "192.168.2.28");
|
|
env.putIfAbsent("DEPLOY_USER", "agiledancers-deploy");
|
|
env.putIfAbsent("DEPLOY_SSH_PORT", "22");
|
|
env.putIfAbsent("DEPLOY_DIRECTORY", "/volume1/docker/agiledancers-production");
|
|
env.putIfAbsent("DEPLOY_BIND_IP", "192.168.2.28");
|
|
env.putIfAbsent("DEPLOY_FRONTEND_PORT", "8088");
|
|
env.putIfAbsent("DEPLOY_REGISTRY", "127.0.0.1:3000");
|
|
Path config = Setup.home().resolve(".config/agiledancers-runner");
|
|
env.putIfAbsent("GITEA_ADMIN_TOKEN_FILE", config.resolve("admin-token").toString());
|
|
env.putIfAbsent("RELEASE_TOKEN_FILE", config.resolve("s6-release-token").toString());
|
|
env.putIfAbsent("PULL_TOKEN_FILE", config.resolve("s6-pull-token").toString());
|
|
env.putIfAbsent("DEPLOY_SSH_KEY_FILE", config.resolve("s6-deploy-key").toString());
|
|
String admin = Setup.secret(env, "GITEA_ADMIN_TOKEN_FILE");
|
|
String pull = Setup.secret(env, "PULL_TOKEN_FILE");
|
|
env.put("RELEASE_TOKEN", Setup.secret(env, "RELEASE_TOKEN_FILE"));
|
|
env.put("DEPLOY_SSH_KEY", Setup.secret(env, "DEPLOY_SSH_KEY_FILE"));
|
|
String bind = env.get("DEPLOY_BIND_IP"), port = env.get("DEPLOY_FRONTEND_PORT");
|
|
Pipeline.check(
|
|
bind.matches("[0-9.]+")
|
|
&& port.matches("[0-9]{4,5}")
|
|
&& Integer.parseInt(port) >= 1024
|
|
&& Integer.parseInt(port) <= 65535,
|
|
"Invalid private bind address or port");
|
|
var commands = new Commands(Duration.ofMinutes(15));
|
|
Path diagnostics = root.resolve("ci/artifacts/deployment");
|
|
Files.createDirectories(diagnostics);
|
|
commands.log = diagnostics.resolve("setup.log");
|
|
Files.writeString(commands.log, "");
|
|
String host = env.get("DEPLOY_HOST");
|
|
String lookup = env.get("DEPLOY_SSH_PORT").equals("22") ? host : "[" + host + "]:" + env.get("DEPLOY_SSH_PORT");
|
|
env.put(
|
|
"DEPLOY_KNOWN_HOSTS",
|
|
commands.execute(
|
|
List.of(
|
|
"ssh-keygen",
|
|
"-F",
|
|
lookup,
|
|
"-f",
|
|
Setup.home().resolve(".ssh/known_hosts").toString()),
|
|
root,
|
|
env,
|
|
null,
|
|
false)
|
|
.checked());
|
|
Pipeline.check(!env.get("DEPLOY_KNOWN_HOSTS").isBlank(), "A previously trusted NAS hostkey is required");
|
|
var api = new ReleaseApi(env);
|
|
api.verified(api.head());
|
|
var uri = URI.create(api.server);
|
|
String registry = env.get("DEPLOY_REGISTRY");
|
|
int registryPort = uri.getPort() >= 0 ? uri.getPort() : uri.getScheme().equals("https") ? 443 : 80;
|
|
Pipeline.check(
|
|
registry.equals(uri.getRawAuthority()) || registry.equals("127.0.0.1:" + registryPort),
|
|
"Deployment registry must be the Gitea registry or its NAS loopback alias");
|
|
String registryUser =
|
|
env.getOrDefault("PULL_USER", env.get("CI_REPOSITORY").split("/")[0]);
|
|
Pipeline.check(registryUser.matches("[a-zA-Z0-9_.-]+"), "Invalid registry user");
|
|
try (var remote = new ReleaseRemote(root, env, commands, "setup")) {
|
|
System.out.println(remote.executeScript(
|
|
Files.readString(root.resolve("deploy/provision.sh")),
|
|
pull + "\n",
|
|
300,
|
|
List.of(remote.directory, registryUser, registry, bind, port)));
|
|
remote.acquire();
|
|
try {
|
|
remote.call("preflight", null);
|
|
} finally {
|
|
remote.release();
|
|
}
|
|
}
|
|
// Provision credentials only after successful NAS login and release-read verification.
|
|
for (String secret : List.of("RELEASE_TOKEN", "DEPLOY_SSH_KEY", "DEPLOY_KNOWN_HOSTS"))
|
|
Setup.api(
|
|
api.server, admin, "PUT", api.base + "/actions/secrets/" + secret, Map.of("data", env.get(secret)));
|
|
for (String variable :
|
|
List.of("DEPLOY_HOST", "DEPLOY_USER", "DEPLOY_SSH_PORT", "DEPLOY_DIRECTORY", "DEPLOY_REGISTRY"))
|
|
variable(api, admin, variable, env.get(variable));
|
|
System.out.println(
|
|
"Production configuration and Gitea deployment secrets/variables installed; no release was created.");
|
|
}
|
|
|
|
static void variable(ReleaseApi api, String admin, String name, String value) throws Exception {
|
|
String path = api.base + "/actions/variables/" + name;
|
|
boolean exists;
|
|
try {
|
|
Setup.api(api.server, admin, "GET", path, null);
|
|
exists = true;
|
|
} catch (Setup.ApiFailure error) {
|
|
if (error.status != 404) throw error;
|
|
exists = false;
|
|
}
|
|
Setup.api(api.server, admin, exists ? "PUT" : "POST", path, Map.of("value", value));
|
|
}
|
|
}
|