first pass
This commit is contained in:
29
src/config.ts
Normal file
29
src/config.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import userConfig from "../config";
|
||||
import hash from "object-hash";
|
||||
|
||||
export type DeployerConfig = {
|
||||
services: {
|
||||
[host: string]: {
|
||||
user: string;
|
||||
repo: string;
|
||||
branch: string;
|
||||
};
|
||||
};
|
||||
giteaUrl: string;
|
||||
directory: string;
|
||||
basePort: number;
|
||||
token: string;
|
||||
systemServicesDir: string;
|
||||
};
|
||||
|
||||
export const config = userConfig as DeployerConfig;
|
||||
export const indexedConfig = Object.fromEntries(
|
||||
Object.entries(config.services).map(([host, service], i) => [
|
||||
hash(service),
|
||||
{
|
||||
...service,
|
||||
host,
|
||||
port: config.basePort + i,
|
||||
},
|
||||
])
|
||||
);
|
||||
68
src/deploy.ts
Normal file
68
src/deploy.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { $ } from "bun";
|
||||
import path from "path";
|
||||
import { config } from "./config";
|
||||
|
||||
type DeployInstance = {
|
||||
host: string;
|
||||
user: string;
|
||||
repo: string;
|
||||
branch: string;
|
||||
commitHash: string;
|
||||
port: number;
|
||||
};
|
||||
|
||||
export const deploy = async ({
|
||||
host,
|
||||
user,
|
||||
repo,
|
||||
branch,
|
||||
port,
|
||||
commitHash,
|
||||
}: DeployInstance) => {
|
||||
const deploymentId = new Date().toISOString();
|
||||
|
||||
const serviceDir = path.join(config.directory, host);
|
||||
|
||||
// Fetch
|
||||
await $`
|
||||
cd ${serviceDir}
|
||||
mkdir -p src
|
||||
mkdir -p logs
|
||||
git clone \
|
||||
-b ${branch} \
|
||||
http://deployer:${config.token}@${config.giteaUrl}/${user}/${repo} \
|
||||
${serviceDir}/src
|
||||
cd src
|
||||
git fetch origin ${branch}
|
||||
git reset --hard origin/${branch}
|
||||
git checkout ${commitHash}
|
||||
`;
|
||||
|
||||
// Build
|
||||
await $`
|
||||
cd ${serviceDir}/src
|
||||
make build
|
||||
`;
|
||||
|
||||
// Register service
|
||||
const systemdServiceName = `deployer-${host}.service`;
|
||||
await $`
|
||||
cat template.service | envsubst > ${serviceDir}/${systemdServiceName}
|
||||
ln -sf ${serviceDir}/${systemdServiceName} ${config.systemServicesDir}/${systemdServiceName}
|
||||
`.env({
|
||||
host,
|
||||
port: port.toString(),
|
||||
repo,
|
||||
branch,
|
||||
commitHash,
|
||||
deploymentId,
|
||||
logsDir: path.join(serviceDir, "logs"),
|
||||
serviceDir,
|
||||
});
|
||||
|
||||
// Start!
|
||||
await $`
|
||||
systemctl daemon-reload
|
||||
systemctl restart ${systemdServiceName}
|
||||
`;
|
||||
};
|
||||
216
src/giteaTypes.ts
Normal file
216
src/giteaTypes.ts
Normal file
@@ -0,0 +1,216 @@
|
||||
export interface PushEvent {
|
||||
ref: string;
|
||||
before: string;
|
||||
after: string;
|
||||
compare_url: string;
|
||||
commits: Commit[];
|
||||
total_commits: number;
|
||||
head_commit: HeadCommit;
|
||||
repository: Repository;
|
||||
pusher: Pusher;
|
||||
sender: Sender;
|
||||
}
|
||||
|
||||
export interface Commit {
|
||||
id: string;
|
||||
message: string;
|
||||
url: string;
|
||||
author: Author;
|
||||
committer: Committer;
|
||||
verification: any;
|
||||
timestamp: string;
|
||||
added: any;
|
||||
removed: any;
|
||||
modified: any;
|
||||
}
|
||||
|
||||
export interface Author {
|
||||
name: string;
|
||||
email: string;
|
||||
username: string;
|
||||
}
|
||||
|
||||
export interface Committer {
|
||||
name: string;
|
||||
email: string;
|
||||
username: string;
|
||||
}
|
||||
|
||||
export interface HeadCommit {
|
||||
id: string;
|
||||
message: string;
|
||||
url: string;
|
||||
author: Author2;
|
||||
committer: Committer2;
|
||||
verification: any;
|
||||
timestamp: string;
|
||||
added: any;
|
||||
removed: any;
|
||||
modified: any;
|
||||
}
|
||||
|
||||
export interface Author2 {
|
||||
name: string;
|
||||
email: string;
|
||||
username: string;
|
||||
}
|
||||
|
||||
export interface Committer2 {
|
||||
name: string;
|
||||
email: string;
|
||||
username: string;
|
||||
}
|
||||
|
||||
export interface Repository {
|
||||
id: number;
|
||||
owner: Owner;
|
||||
name: string;
|
||||
full_name: string;
|
||||
description: string;
|
||||
empty: boolean;
|
||||
private: boolean;
|
||||
fork: boolean;
|
||||
template: boolean;
|
||||
mirror: boolean;
|
||||
size: number;
|
||||
language: string;
|
||||
languages_url: string;
|
||||
html_url: string;
|
||||
url: string;
|
||||
link: string;
|
||||
ssh_url: string;
|
||||
clone_url: string;
|
||||
original_url: string;
|
||||
website: string;
|
||||
stars_count: number;
|
||||
forks_count: number;
|
||||
watchers_count: number;
|
||||
open_issues_count: number;
|
||||
open_pr_counter: number;
|
||||
release_counter: number;
|
||||
default_branch: string;
|
||||
archived: boolean;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
archived_at: string;
|
||||
permissions: Permissions;
|
||||
has_code: boolean;
|
||||
has_issues: boolean;
|
||||
internal_tracker: InternalTracker;
|
||||
has_wiki: boolean;
|
||||
has_pull_requests: boolean;
|
||||
has_projects: boolean;
|
||||
projects_mode: string;
|
||||
has_releases: boolean;
|
||||
has_packages: boolean;
|
||||
has_actions: boolean;
|
||||
ignore_whitespace_conflicts: boolean;
|
||||
allow_merge_commits: boolean;
|
||||
allow_rebase: boolean;
|
||||
allow_rebase_explicit: boolean;
|
||||
allow_squash_merge: boolean;
|
||||
allow_fast_forward_only_merge: boolean;
|
||||
allow_rebase_update: boolean;
|
||||
allow_manual_merge: boolean;
|
||||
autodetect_manual_merge: boolean;
|
||||
default_delete_branch_after_merge: boolean;
|
||||
default_merge_style: string;
|
||||
default_allow_maintainer_edit: boolean;
|
||||
avatar_url: string;
|
||||
internal: boolean;
|
||||
mirror_interval: string;
|
||||
object_format_name: string;
|
||||
mirror_updated: string;
|
||||
topics: any[];
|
||||
licenses: any[];
|
||||
}
|
||||
|
||||
export interface Owner {
|
||||
id: number;
|
||||
login: string;
|
||||
login_name: string;
|
||||
source_id: number;
|
||||
full_name: string;
|
||||
email: string;
|
||||
avatar_url: string;
|
||||
html_url: string;
|
||||
language: string;
|
||||
is_admin: boolean;
|
||||
last_login: string;
|
||||
created: string;
|
||||
restricted: boolean;
|
||||
active: boolean;
|
||||
prohibit_login: boolean;
|
||||
location: string;
|
||||
website: string;
|
||||
description: string;
|
||||
visibility: string;
|
||||
followers_count: number;
|
||||
following_count: number;
|
||||
starred_repos_count: number;
|
||||
username: string;
|
||||
}
|
||||
|
||||
export interface Permissions {
|
||||
admin: boolean;
|
||||
push: boolean;
|
||||
pull: boolean;
|
||||
}
|
||||
|
||||
export interface InternalTracker {
|
||||
enable_time_tracker: boolean;
|
||||
allow_only_contributors_to_track_time: boolean;
|
||||
enable_issue_dependencies: boolean;
|
||||
}
|
||||
|
||||
export interface Pusher {
|
||||
id: number;
|
||||
login: string;
|
||||
login_name: string;
|
||||
source_id: number;
|
||||
full_name: string;
|
||||
email: string;
|
||||
avatar_url: string;
|
||||
html_url: string;
|
||||
language: string;
|
||||
is_admin: boolean;
|
||||
last_login: string;
|
||||
created: string;
|
||||
restricted: boolean;
|
||||
active: boolean;
|
||||
prohibit_login: boolean;
|
||||
location: string;
|
||||
website: string;
|
||||
description: string;
|
||||
visibility: string;
|
||||
followers_count: number;
|
||||
following_count: number;
|
||||
starred_repos_count: number;
|
||||
username: string;
|
||||
}
|
||||
|
||||
export interface Sender {
|
||||
id: number;
|
||||
login: string;
|
||||
login_name: string;
|
||||
source_id: number;
|
||||
full_name: string;
|
||||
email: string;
|
||||
avatar_url: string;
|
||||
html_url: string;
|
||||
language: string;
|
||||
is_admin: boolean;
|
||||
last_login: string;
|
||||
created: string;
|
||||
restricted: boolean;
|
||||
active: boolean;
|
||||
prohibit_login: boolean;
|
||||
location: string;
|
||||
website: string;
|
||||
description: string;
|
||||
visibility: string;
|
||||
followers_count: number;
|
||||
following_count: number;
|
||||
starred_repos_count: number;
|
||||
username: string;
|
||||
}
|
||||
42
src/index.ts
Normal file
42
src/index.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { Elysia } from "elysia";
|
||||
import hash from "object-hash";
|
||||
import { indexedConfig } from "./config";
|
||||
import { deploy } from "./deploy";
|
||||
import { PushEvent } from "./giteaTypes";
|
||||
|
||||
const port = 6001;
|
||||
|
||||
new Elysia()
|
||||
.onError(({ error }) => console.error(error))
|
||||
.onRequest(({ request }) => {
|
||||
console.log(request.method, new URL(request.url).pathname);
|
||||
})
|
||||
.get("/ping", () => "pong")
|
||||
.post("/gitea", ({ body }) => {
|
||||
const event = body as PushEvent;
|
||||
|
||||
const user = event.repository.owner.username;
|
||||
const repo = event.repository.name;
|
||||
const branch = event.ref.replace(/^(refs\/heads\/)/, "");
|
||||
const commitHash = event.before;
|
||||
|
||||
const service =
|
||||
indexedConfig[
|
||||
hash({
|
||||
user,
|
||||
repo,
|
||||
branch,
|
||||
})
|
||||
];
|
||||
|
||||
if (service == null) {
|
||||
console.error(`No service defined for ${user}/${repo}:${branch}`);
|
||||
return;
|
||||
}
|
||||
|
||||
void deploy({ ...service, commitHash });
|
||||
return "deploying";
|
||||
})
|
||||
.listen(port);
|
||||
|
||||
console.log(`started deployer2 on :${port}`);
|
||||
Reference in New Issue
Block a user