S19: establish engineering repository
This commit is contained in:
105
resources/ci/ReleaseSetup.java
Normal file
105
resources/ci/ReleaseSetup.java
Normal file
@@ -0,0 +1,105 @@
|
||||
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("DEPLOY_SSH_PORT", "22");
|
||||
Path config = Setup.home().resolve(".config/" + Recipe.project(env) + "-runner");
|
||||
env.putIfAbsent("GITEA_ADMIN_TOKEN_FILE", config.resolve("admin-token").toString());
|
||||
env.putIfAbsent("RELEASE_TOKEN_FILE", config.resolve("release-token").toString());
|
||||
env.putIfAbsent("PULL_TOKEN_FILE", config.resolve("pull-token").toString());
|
||||
env.putIfAbsent("DEPLOY_SSH_KEY_FILE", config.resolve("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("generated/deploy/provision.sh")),
|
||||
pull + "\n",
|
||||
300,
|
||||
List.of(
|
||||
remote.directory,
|
||||
registryUser,
|
||||
registry,
|
||||
bind,
|
||||
port,
|
||||
Setup.required(env, "DEPLOY_DOCKER_BIN"))));
|
||||
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));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user