mirror of
https://github.com/n8n-io/n8n.git
synced 2026-05-30 16:26:59 +02:00
259 lines
6.6 KiB
TypeScript
259 lines
6.6 KiB
TypeScript
import type { StartedNetwork, StartedTestContainer } from 'testcontainers';
|
|
import { GenericContainer, Wait } from 'testcontainers';
|
|
|
|
import { createSilentLogConsumer } from '../helpers/utils';
|
|
import { TEST_CONTAINER_IMAGES } from '../test-containers';
|
|
import type { HelperContext, Service, ServiceResult } from './types';
|
|
|
|
const HOSTNAME = 'gitea';
|
|
const HTTP_PORT = 3000;
|
|
const SSH_PORT = 22;
|
|
const DEFAULT_ADMIN = 'giteaadmin';
|
|
const DEFAULT_PASSWORD = 'giteapassword';
|
|
const DEFAULT_EMAIL = 'admin@example.com';
|
|
const DEFAULT_REPO = 'n8n-test-repo';
|
|
const DEFAULT_BRANCHES = ['development', 'staging', 'production'];
|
|
|
|
export interface GiteaMeta {
|
|
apiUrl: string;
|
|
adminUsername: string;
|
|
adminPassword: string;
|
|
defaultRepo: string;
|
|
}
|
|
|
|
export type GiteaResult = ServiceResult<GiteaMeta>;
|
|
|
|
export const gitea: Service<GiteaResult> = {
|
|
description: 'Git server (Gitea)',
|
|
|
|
async start(network: StartedNetwork, projectName: string): Promise<GiteaResult> {
|
|
const { consumer, throwWithLogs } = createSilentLogConsumer();
|
|
|
|
try {
|
|
const container = await new GenericContainer(TEST_CONTAINER_IMAGES.gitea)
|
|
.withNetwork(network)
|
|
.withNetworkAliases(HOSTNAME)
|
|
.withExposedPorts(HTTP_PORT, SSH_PORT)
|
|
.withEnvironment({
|
|
GITEA__database__DB_TYPE: 'sqlite3',
|
|
GITEA__server__DOMAIN: HOSTNAME,
|
|
GITEA__server__ROOT_URL: `http://${HOSTNAME}:${HTTP_PORT}/`,
|
|
GITEA__server__SSH_DOMAIN: HOSTNAME,
|
|
GITEA__security__INSTALL_LOCK: 'true',
|
|
GITEA__security__SECRET_KEY: 'gitea-test-secret-key',
|
|
GITEA__service__DISABLE_REGISTRATION: 'true',
|
|
})
|
|
.withWaitStrategy(Wait.forListeningPorts())
|
|
.withLabels({
|
|
'com.docker.compose.project': projectName,
|
|
'com.docker.compose.service': HOSTNAME,
|
|
})
|
|
.withName(`${projectName}-${HOSTNAME}`)
|
|
.withReuse()
|
|
.withLogConsumer(consumer)
|
|
.start();
|
|
|
|
// Setup admin user and default repo
|
|
await addUser(container, DEFAULT_ADMIN, DEFAULT_PASSWORD, DEFAULT_EMAIL, true);
|
|
await addRepo(container, DEFAULT_REPO, DEFAULT_ADMIN, DEFAULT_PASSWORD);
|
|
|
|
// Create default branches
|
|
for (const branch of DEFAULT_BRANCHES) {
|
|
await addBranch(container, DEFAULT_REPO, branch, DEFAULT_ADMIN, DEFAULT_PASSWORD);
|
|
}
|
|
|
|
return {
|
|
container,
|
|
meta: {
|
|
apiUrl: `http://${container.getHost()}:${container.getMappedPort(HTTP_PORT)}`,
|
|
adminUsername: DEFAULT_ADMIN,
|
|
adminPassword: DEFAULT_PASSWORD,
|
|
defaultRepo: DEFAULT_REPO,
|
|
},
|
|
};
|
|
} catch (error) {
|
|
return throwWithLogs(error);
|
|
}
|
|
},
|
|
|
|
env(result: GiteaResult, external?: boolean): Record<string, string> {
|
|
return {
|
|
N8N_SOURCECONTROL_HOST: external ? result.meta.apiUrl : `http://${HOSTNAME}:${HTTP_PORT}`,
|
|
};
|
|
},
|
|
};
|
|
|
|
async function addUser(
|
|
container: StartedTestContainer,
|
|
username: string,
|
|
password: string,
|
|
email: string,
|
|
admin = false,
|
|
): Promise<void> {
|
|
const adminFlag = admin ? '--admin' : '';
|
|
await container.exec([
|
|
'bash',
|
|
'-c',
|
|
`cd /data/gitea && su git -c "/usr/local/bin/gitea admin user create --config /data/gitea/conf/app.ini --username ${username} --password ${password} --email ${email} ${adminFlag} --must-change-password=false"`,
|
|
]);
|
|
}
|
|
|
|
async function addRepo(
|
|
container: StartedTestContainer,
|
|
repoName: string,
|
|
username: string,
|
|
password: string,
|
|
): Promise<void> {
|
|
await container.exec([
|
|
'curl',
|
|
'-X',
|
|
'POST',
|
|
`http://localhost:${HTTP_PORT}/api/v1/user/repos`,
|
|
'-H',
|
|
'Content-Type: application/json',
|
|
'-u',
|
|
`${username}:${password}`,
|
|
'-d',
|
|
`{"name":"${repoName}","private":false,"auto_init":true}`,
|
|
]);
|
|
}
|
|
|
|
async function addBranch(
|
|
container: StartedTestContainer,
|
|
repoName: string,
|
|
branchName: string,
|
|
username: string,
|
|
password: string,
|
|
fromBranch = 'main',
|
|
): Promise<void> {
|
|
await container.exec([
|
|
'curl',
|
|
'-X',
|
|
'POST',
|
|
`http://localhost:${HTTP_PORT}/api/v1/repos/${username}/${repoName}/branches`,
|
|
'-H',
|
|
'Content-Type: application/json',
|
|
'-u',
|
|
`${username}:${password}`,
|
|
'-d',
|
|
`{"new_branch_name":"${branchName}","old_branch_name":"${fromBranch}"}`,
|
|
]);
|
|
}
|
|
|
|
export class GiteaHelper {
|
|
private readonly container: StartedTestContainer;
|
|
private readonly meta: GiteaMeta;
|
|
|
|
constructor(container: StartedTestContainer, meta: GiteaMeta) {
|
|
this.container = container;
|
|
this.meta = meta;
|
|
}
|
|
|
|
get apiUrl(): string {
|
|
return this.meta.apiUrl;
|
|
}
|
|
|
|
get adminUsername(): string {
|
|
return this.meta.adminUsername;
|
|
}
|
|
|
|
get adminPassword(): string {
|
|
return this.meta.adminPassword;
|
|
}
|
|
|
|
get defaultRepo(): string {
|
|
return this.meta.defaultRepo;
|
|
}
|
|
|
|
async createUser(
|
|
username: string,
|
|
password: string,
|
|
email: string,
|
|
admin = false,
|
|
): Promise<void> {
|
|
await addUser(this.container, username, password, email, admin);
|
|
}
|
|
|
|
async createRepo(repoName: string, username?: string, password?: string): Promise<void> {
|
|
await addRepo(
|
|
this.container,
|
|
repoName,
|
|
username ?? this.meta.adminUsername,
|
|
password ?? this.meta.adminPassword,
|
|
);
|
|
}
|
|
|
|
async createBranch(
|
|
repoName: string,
|
|
branchName: string,
|
|
username?: string,
|
|
password?: string,
|
|
fromBranch = 'main',
|
|
): Promise<void> {
|
|
await addBranch(
|
|
this.container,
|
|
repoName,
|
|
branchName,
|
|
username ?? this.meta.adminUsername,
|
|
password ?? this.meta.adminPassword,
|
|
fromBranch,
|
|
);
|
|
}
|
|
|
|
async addSSHKey(
|
|
keyTitle: string,
|
|
publicKey: string,
|
|
username?: string,
|
|
password?: string,
|
|
): Promise<void> {
|
|
await this.container.exec([
|
|
'curl',
|
|
'-X',
|
|
'POST',
|
|
`http://localhost:${HTTP_PORT}/api/v1/user/keys`,
|
|
'-H',
|
|
'Content-Type: application/json',
|
|
'-u',
|
|
`${username ?? this.meta.adminUsername}:${password ?? this.meta.adminPassword}`,
|
|
'-d',
|
|
`{"title":"${keyTitle}","key":"${publicKey}","read_only":false}`,
|
|
]);
|
|
}
|
|
|
|
async commitExists(
|
|
repoName: string,
|
|
commitHash: string,
|
|
username?: string,
|
|
password?: string,
|
|
): Promise<boolean> {
|
|
const result = await this.container.exec([
|
|
'curl',
|
|
'-s',
|
|
'-o',
|
|
'/dev/null',
|
|
'-w',
|
|
'%{http_code}',
|
|
`http://localhost:${HTTP_PORT}/api/v1/repos/${username ?? this.meta.adminUsername}/${repoName}/git/commits/${commitHash}`,
|
|
'-u',
|
|
`${username ?? this.meta.adminUsername}:${password ?? this.meta.adminPassword}`,
|
|
]);
|
|
// curl writes HTTP status code to stdout, 200 means commit exists
|
|
const statusCode = result.output.trim();
|
|
return statusCode === '200';
|
|
}
|
|
}
|
|
|
|
export function createGiteaHelper(ctx: HelperContext): GiteaHelper {
|
|
const result = ctx.serviceResults.gitea as GiteaResult | undefined;
|
|
if (!result) {
|
|
throw new Error('Gitea service not found in context');
|
|
}
|
|
return new GiteaHelper(result.container, result.meta);
|
|
}
|
|
|
|
declare module './types' {
|
|
interface ServiceHelpers {
|
|
gitea: GiteaHelper;
|
|
}
|
|
}
|