Refactor out common code for creating deployment
This commit is contained in:
parent
9d8d2199e2
commit
443a3f2b6e
@ -1,9 +1,9 @@
|
||||
import debug from 'debug';
|
||||
import assert from 'assert';
|
||||
import { inc as semverInc } from 'semver';
|
||||
import debug from 'debug';
|
||||
import { DateTime } from 'luxon';
|
||||
import { DeepPartial } from 'typeorm';
|
||||
import { Octokit } from 'octokit';
|
||||
import { inc as semverInc } from 'semver';
|
||||
import { DeepPartial } from 'typeorm';
|
||||
|
||||
import { Registry as LaconicRegistry, getGasPrice, parseGasAndFees } from '@cerc-io/registry-sdk';
|
||||
|
||||
@ -14,8 +14,8 @@ import {
|
||||
ApplicationDeploymentRequest,
|
||||
ApplicationDeploymentRemovalRequest
|
||||
} from './entity/Deployment';
|
||||
import { AppDeploymentRecord, AppDeploymentRemovalRecord, AuctionData, PackageJSON } from './types';
|
||||
import { getConfig, sleep } from './utils';
|
||||
import { AppDeploymentRecord, AppDeploymentRemovalRecord, AuctionData } from './types';
|
||||
import { getConfig, getRepoDetails, sleep } from './utils';
|
||||
import { Auction } from '@cerc-io/registry-sdk/dist/proto/cerc/auction/v1/auction';
|
||||
|
||||
const log = debug('snowball:registry');
|
||||
@ -33,7 +33,7 @@ export class Registry {
|
||||
private registry: LaconicRegistry;
|
||||
private registryConfig: RegistryConfig;
|
||||
|
||||
constructor (registryConfig: RegistryConfig) {
|
||||
constructor(registryConfig: RegistryConfig) {
|
||||
this.registryConfig = registryConfig;
|
||||
|
||||
const gasPrice = getGasPrice(registryConfig.fee.gasPrice);
|
||||
@ -45,22 +45,21 @@ export class Registry {
|
||||
);
|
||||
}
|
||||
|
||||
async createApplicationRecord ({
|
||||
appName,
|
||||
packageJSON,
|
||||
async createApplicationRecord({
|
||||
octokit,
|
||||
repository,
|
||||
commitHash,
|
||||
appType,
|
||||
repoUrl
|
||||
}: {
|
||||
appName: string;
|
||||
packageJSON: PackageJSON;
|
||||
octokit: Octokit
|
||||
repository: string;
|
||||
commitHash: string;
|
||||
appType: string;
|
||||
repoUrl: string;
|
||||
}): Promise<{
|
||||
applicationRecordId: string;
|
||||
applicationRecordData: ApplicationRecord;
|
||||
}> {
|
||||
const { repo, repoUrl, packageJSON } = await getRepoDetails(octokit, repository, commitHash)
|
||||
// Use registry-sdk to publish record
|
||||
// Reference: https://git.vdb.to/cerc-io/test-progressive-web-app/src/branch/main/scripts/publish-app-record.sh
|
||||
// Fetch previous records
|
||||
@ -94,7 +93,7 @@ export class Registry {
|
||||
repository_ref: commitHash,
|
||||
repository: [repoUrl],
|
||||
app_type: appType,
|
||||
name: appName,
|
||||
name: repo,
|
||||
...(packageJSON.description && { description: packageJSON.description }),
|
||||
...(packageJSON.homepage && { homepage: packageJSON.homepage }),
|
||||
...(packageJSON.license && { license: packageJSON.license }),
|
||||
@ -119,10 +118,11 @@ export class Registry {
|
||||
fee
|
||||
);
|
||||
|
||||
log(`Published application record ${result.id}`);
|
||||
log('Application record data:', applicationRecord);
|
||||
|
||||
// TODO: Discuss computation of LRN
|
||||
const lrn = this.getLrn(appName);
|
||||
const lrn = this.getLrn(repo);
|
||||
log(`Setting name: ${lrn} for record ID: ${result.id}`);
|
||||
|
||||
await sleep(SLEEP_DURATION);
|
||||
@ -161,7 +161,7 @@ export class Registry {
|
||||
};
|
||||
}
|
||||
|
||||
async createApplicationDeploymentAuction (
|
||||
async createApplicationDeploymentAuction(
|
||||
appName: string,
|
||||
octokit: Octokit,
|
||||
auctionData: AuctionData,
|
||||
@ -170,52 +170,15 @@ export class Registry {
|
||||
applicationDeploymentAuctionId: string;
|
||||
}> {
|
||||
assert(data.project?.repository, 'Project repository not found');
|
||||
const [owner, repo] = data.project.repository.split('/');
|
||||
|
||||
const { data: packageJSONData } = await octokit.rest.repos.getContent({
|
||||
owner,
|
||||
repo,
|
||||
path: 'package.json',
|
||||
ref: data.commitHash,
|
||||
await this.createApplicationRecord({
|
||||
octokit,
|
||||
repository: data.project.repository,
|
||||
appType: data.project!.template!,
|
||||
commitHash: data.commitHash!,
|
||||
});
|
||||
|
||||
if (!packageJSONData) {
|
||||
throw new Error('Package.json file not found');
|
||||
}
|
||||
|
||||
assert(!Array.isArray(packageJSONData) && packageJSONData.type === 'file');
|
||||
const packageJSON: PackageJSON = JSON.parse(atob(packageJSONData.content));
|
||||
|
||||
assert(packageJSON.name, "name field doesn't exist in package.json");
|
||||
|
||||
const repoUrl = (
|
||||
await octokit.rest.repos.get({
|
||||
owner,
|
||||
repo,
|
||||
})
|
||||
).data.html_url;
|
||||
|
||||
const { applicationRecordId } =
|
||||
await this.createApplicationRecord({
|
||||
appName: repo,
|
||||
packageJSON,
|
||||
appType: data.project!.template!,
|
||||
commitHash: data.commitHash!,
|
||||
repoUrl,
|
||||
});
|
||||
|
||||
log(
|
||||
`Published application record ${applicationRecordId}`,
|
||||
);
|
||||
|
||||
const lrn = this.getLrn(appName);
|
||||
const records = await this.registry.resolveNames([lrn]);
|
||||
const applicationRecord = records[0];
|
||||
|
||||
if (!applicationRecord) {
|
||||
throw new Error(`No record found for ${lrn}`);
|
||||
}
|
||||
|
||||
const config = await getConfig();
|
||||
const auctionConfig = config.auction;
|
||||
|
||||
@ -264,7 +227,7 @@ export class Registry {
|
||||
};
|
||||
}
|
||||
|
||||
async createApplicationDeploymentRequest (data: {
|
||||
async createApplicationDeploymentRequest(data: {
|
||||
deployment: Deployment,
|
||||
appName: string,
|
||||
repository: string,
|
||||
@ -361,7 +324,7 @@ export class Registry {
|
||||
/**
|
||||
* Fetch ApplicationDeploymentRecords for deployments
|
||||
*/
|
||||
async getDeploymentRecords (
|
||||
async getDeploymentRecords(
|
||||
deployments: Deployment[]
|
||||
): Promise<AppDeploymentRecord[]> {
|
||||
// Fetch ApplicationDeploymentRecords for corresponding ApplicationRecord set in deployments
|
||||
@ -386,7 +349,7 @@ export class Registry {
|
||||
/**
|
||||
* Fetch ApplicationDeploymentRecords by filter
|
||||
*/
|
||||
async getDeploymentRecordsByFilter (filter: { [key: string]: any }): Promise<AppDeploymentRecord[]> {
|
||||
async getDeploymentRecordsByFilter(filter: { [key: string]: any }): Promise<AppDeploymentRecord[]> {
|
||||
return this.registry.queryRecords(
|
||||
{
|
||||
type: APP_DEPLOYMENT_RECORD_TYPE,
|
||||
@ -399,7 +362,7 @@ export class Registry {
|
||||
/**
|
||||
* Fetch ApplicationDeploymentRemovalRecords for deployments
|
||||
*/
|
||||
async getDeploymentRemovalRecords (
|
||||
async getDeploymentRemovalRecords(
|
||||
deployments: Deployment[]
|
||||
): Promise<AppDeploymentRemovalRecord[]> {
|
||||
// Fetch ApplicationDeploymentRemovalRecords for corresponding ApplicationDeploymentRecord set in deployments
|
||||
@ -420,7 +383,7 @@ export class Registry {
|
||||
);
|
||||
}
|
||||
|
||||
async createApplicationDeploymentRemovalRequest (data: {
|
||||
async createApplicationDeploymentRemovalRequest(data: {
|
||||
deploymentId: string;
|
||||
deployerLrn: string;
|
||||
}): Promise<{
|
||||
|
@ -6,7 +6,7 @@ import { Octokit, RequestError } from 'octokit';
|
||||
import { OAuthApp } from '@octokit/oauth-app';
|
||||
|
||||
import { Database } from './database';
|
||||
import { Deployment, DeploymentStatus, Environment } from './entity/Deployment';
|
||||
import { ApplicationRecord, Deployment, DeploymentStatus, Environment } from './entity/Deployment';
|
||||
import { Domain } from './entity/Domain';
|
||||
import { EnvironmentVariable } from './entity/EnvironmentVariable';
|
||||
import { Organization } from './entity/Organization';
|
||||
@ -21,9 +21,9 @@ import {
|
||||
AppDeploymentRemovalRecord,
|
||||
AuctionData,
|
||||
GitPushEventPayload,
|
||||
PackageJSON,
|
||||
} from './types';
|
||||
import { Role } from './entity/UserOrganization';
|
||||
import { getRepoDetails } from './utils';
|
||||
|
||||
const log = debug('snowball:service');
|
||||
|
||||
@ -598,45 +598,20 @@ export class Service {
|
||||
userId: string,
|
||||
octokit: Octokit,
|
||||
data: DeepPartial<Deployment>,
|
||||
lrn: string
|
||||
deployerLrn: string
|
||||
): Promise<Deployment> {
|
||||
assert(data.project?.repository, 'Project repository not found');
|
||||
log(
|
||||
`Creating deployment in project ${data.project.name} from branch ${data.branch}`,
|
||||
);
|
||||
const [owner, repo] = data.project.repository.split('/');
|
||||
|
||||
const { data: packageJSONData } = await octokit.rest.repos.getContent({
|
||||
owner,
|
||||
repo,
|
||||
path: 'package.json',
|
||||
ref: data.commitHash,
|
||||
});
|
||||
|
||||
if (!packageJSONData) {
|
||||
throw new Error('Package.json file not found');
|
||||
}
|
||||
|
||||
assert(!Array.isArray(packageJSONData) && packageJSONData.type === 'file');
|
||||
const packageJSON: PackageJSON = JSON.parse(atob(packageJSONData.content));
|
||||
|
||||
assert(packageJSON.name, "name field doesn't exist in package.json");
|
||||
|
||||
const repoUrl = (
|
||||
await octokit.rest.repos.get({
|
||||
owner,
|
||||
repo,
|
||||
})
|
||||
).data.html_url;
|
||||
|
||||
// TODO: Set environment variables for each deployment (environment variables can`t be set in application record)
|
||||
const { applicationRecordId, applicationRecordData } =
|
||||
await this.laconicRegistry.createApplicationRecord({
|
||||
appName: repo,
|
||||
packageJSON,
|
||||
octokit,
|
||||
repository: data.project.repository,
|
||||
appType: data.project!.template!,
|
||||
commitHash: data.commitHash!,
|
||||
repoUrl,
|
||||
});
|
||||
|
||||
// Update previous deployment with prod branch domain
|
||||
@ -652,40 +627,10 @@ export class Service {
|
||||
);
|
||||
}
|
||||
|
||||
const newDeployment = await this.db.addDeployment({
|
||||
project: data.project,
|
||||
branch: data.branch,
|
||||
commitHash: data.commitHash,
|
||||
commitMessage: data.commitMessage,
|
||||
environment: data.environment,
|
||||
status: DeploymentStatus.Building,
|
||||
applicationRecordId,
|
||||
applicationRecordData,
|
||||
domain: data.domain,
|
||||
createdBy: Object.assign(new User(), {
|
||||
id: userId,
|
||||
}),
|
||||
deployerLrn: lrn,
|
||||
});
|
||||
|
||||
log(
|
||||
`Created deployment ${newDeployment.id} and published application record ${applicationRecordId}`,
|
||||
);
|
||||
|
||||
const environmentVariables =
|
||||
await this.db.getEnvironmentVariablesByProjectId(data.project.id!, {
|
||||
environment: Environment.Production,
|
||||
});
|
||||
|
||||
const environmentVariablesObj = environmentVariables.reduce(
|
||||
(acc, env) => {
|
||||
acc[env.key] = env.value;
|
||||
|
||||
return acc;
|
||||
},
|
||||
{} as { [key: string]: string },
|
||||
);
|
||||
const newDeployment = await this.createDeploymentFromData(userId, data, deployerLrn, applicationRecordId, applicationRecordData);
|
||||
|
||||
const { repo, repoUrl } = await getRepoDetails(octokit, data.project.repository, data.commitHash);
|
||||
const environmentVariablesObj = await this.getEnvVariables(data.project!.id!);
|
||||
// To set project DNS
|
||||
if (data.environment === Environment.Production) {
|
||||
// On deleting deployment later, project DNS deployment is also deleted
|
||||
@ -696,7 +641,7 @@ export class Service {
|
||||
repository: repoUrl,
|
||||
environmentVariables: environmentVariablesObj,
|
||||
dns: `${newDeployment.project.name}`,
|
||||
lrn
|
||||
lrn: deployerLrn
|
||||
});
|
||||
}
|
||||
|
||||
@ -705,7 +650,7 @@ export class Service {
|
||||
deployment: newDeployment,
|
||||
appName: repo,
|
||||
repository: repoUrl,
|
||||
lrn,
|
||||
lrn: deployerLrn,
|
||||
environmentVariables: environmentVariablesObj,
|
||||
dns: `${newDeployment.project.name}-${newDeployment.id}`,
|
||||
});
|
||||
@ -716,8 +661,8 @@ export class Service {
|
||||
});
|
||||
|
||||
// Save deployer lrn only if present
|
||||
if (lrn) {
|
||||
newDeployment.project.deployerLrns = [lrn];
|
||||
if (deployerLrn) {
|
||||
newDeployment.project.deployerLrns = [deployerLrn];
|
||||
}
|
||||
|
||||
return newDeployment;
|
||||
@ -725,7 +670,7 @@ export class Service {
|
||||
|
||||
async createDeploymentFromAuction(
|
||||
project: DeepPartial<Project>,
|
||||
deployer: string
|
||||
deployerLrn: string
|
||||
): Promise<Deployment> {
|
||||
const octokit = await this.getOctokit(project.ownerId!);
|
||||
const [owner, repo] = project.repository!.split('/');
|
||||
@ -761,40 +706,9 @@ export class Service {
|
||||
commitMessage: latestCommit.commit.message,
|
||||
};
|
||||
|
||||
const newDeployment = await this.db.addDeployment({
|
||||
project: project,
|
||||
branch: deploymentData.branch,
|
||||
commitHash: deploymentData.commitHash,
|
||||
commitMessage: deploymentData.commitMessage,
|
||||
environment: deploymentData.environment,
|
||||
status: DeploymentStatus.Building,
|
||||
applicationRecordId,
|
||||
applicationRecordData,
|
||||
domain: deploymentData.domain,
|
||||
createdBy: Object.assign(new User(), {
|
||||
id: project.ownerId!,
|
||||
}),
|
||||
deployerLrn: deployer,
|
||||
});
|
||||
|
||||
log(
|
||||
`Created deployment ${newDeployment.id} and published application record ${applicationRecordId}`,
|
||||
);
|
||||
|
||||
const environmentVariables =
|
||||
await this.db.getEnvironmentVariablesByProjectId(project!.id!, {
|
||||
environment: Environment.Production,
|
||||
});
|
||||
|
||||
const environmentVariablesObj = environmentVariables.reduce(
|
||||
(acc, env) => {
|
||||
acc[env.key] = env.value;
|
||||
|
||||
return acc;
|
||||
},
|
||||
{} as { [key: string]: string },
|
||||
);
|
||||
const newDeployment = await this.createDeploymentFromData(project.ownerId!, deploymentData, deployerLrn, applicationRecordId, applicationRecordData);
|
||||
|
||||
const environmentVariablesObj = await this.getEnvVariables(project!.id!);
|
||||
// To set project DNS
|
||||
if (deploymentData.environment === Environment.Production) {
|
||||
// On deleting deployment later, project DNS deployment is also deleted
|
||||
@ -806,7 +720,7 @@ export class Service {
|
||||
environmentVariables: environmentVariablesObj,
|
||||
dns: `${newDeployment.project.name}`,
|
||||
auctionId: project.auctionId!,
|
||||
lrn: deployer,
|
||||
lrn: deployerLrn,
|
||||
});
|
||||
}
|
||||
|
||||
@ -817,7 +731,7 @@ export class Service {
|
||||
appName: repo,
|
||||
repository: repoUrl,
|
||||
auctionId: project.auctionId!,
|
||||
lrn: deployer,
|
||||
lrn: deployerLrn,
|
||||
environmentVariables: environmentVariablesObj,
|
||||
dns: `${newDeployment.project.name}-${newDeployment.id}`,
|
||||
});
|
||||
@ -830,6 +744,34 @@ export class Service {
|
||||
return newDeployment;
|
||||
}
|
||||
|
||||
async createDeploymentFromData(
|
||||
userId: string,
|
||||
data: DeepPartial<Deployment>,
|
||||
deployerLrn: string,
|
||||
applicationRecordId: string,
|
||||
applicationRecordData: ApplicationRecord,
|
||||
): Promise<Deployment> {
|
||||
const newDeployment = await this.db.addDeployment({
|
||||
project: data.project,
|
||||
branch: data.branch,
|
||||
commitHash: data.commitHash,
|
||||
commitMessage: data.commitMessage,
|
||||
environment: data.environment,
|
||||
status: DeploymentStatus.Building,
|
||||
applicationRecordId,
|
||||
applicationRecordData,
|
||||
domain: data.domain,
|
||||
createdBy: Object.assign(new User(), {
|
||||
id: userId,
|
||||
}),
|
||||
deployerLrn,
|
||||
});
|
||||
|
||||
log(`Created deployment ${newDeployment.id}`);
|
||||
|
||||
return newDeployment;
|
||||
}
|
||||
|
||||
async addProjectFromTemplate(
|
||||
user: User,
|
||||
organizationSlug: string,
|
||||
@ -1297,6 +1239,24 @@ export class Service {
|
||||
return this.db.updateUser(user, data);
|
||||
}
|
||||
|
||||
async getEnvVariables(
|
||||
projectId: string,
|
||||
): Promise<{ [key: string]: string }> {
|
||||
const environmentVariables = await this.db.getEnvironmentVariablesByProjectId(projectId, {
|
||||
environment: Environment.Production,
|
||||
});
|
||||
|
||||
const environmentVariablesObj = environmentVariables.reduce(
|
||||
(acc, env) => {
|
||||
acc[env.key] = env.value;
|
||||
return acc;
|
||||
},
|
||||
{} as { [key: string]: string },
|
||||
);
|
||||
|
||||
return environmentVariablesObj;
|
||||
}
|
||||
|
||||
async getAuctionData(
|
||||
auctionId: string
|
||||
): Promise<any> {
|
||||
|
@ -1,11 +1,14 @@
|
||||
import assert from 'assert';
|
||||
import debug from 'debug';
|
||||
import fs from 'fs-extra';
|
||||
import { Octokit } from 'octokit';
|
||||
import path from 'path';
|
||||
import toml from 'toml';
|
||||
import debug from 'debug';
|
||||
import { DataSource, DeepPartial, EntityTarget, ObjectLiteral } from 'typeorm';
|
||||
|
||||
import { Config } from './config';
|
||||
import { DEFAULT_CONFIG_FILE_PATH } from './constants';
|
||||
import { PackageJSON } from './types';
|
||||
|
||||
const log = debug('snowball:utils');
|
||||
|
||||
@ -77,3 +80,43 @@ export const loadAndSaveData = async <Entity extends ObjectLiteral>(
|
||||
|
||||
export const sleep = async (ms: number): Promise<void> =>
|
||||
new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
export const getRepoDetails = async (
|
||||
octokit: Octokit,
|
||||
repository: string,
|
||||
commitHash: string | undefined,
|
||||
): Promise<{
|
||||
repo: string;
|
||||
packageJSON: PackageJSON;
|
||||
repoUrl: string;
|
||||
}> => {
|
||||
const [owner, repo] = repository.split('/');
|
||||
const { data: packageJSONData } = await octokit.rest.repos.getContent({
|
||||
owner,
|
||||
repo,
|
||||
path: 'package.json',
|
||||
ref: commitHash,
|
||||
});
|
||||
|
||||
if (!packageJSONData) {
|
||||
throw new Error('Package.json file not found');
|
||||
}
|
||||
|
||||
assert(!Array.isArray(packageJSONData) && packageJSONData.type === 'file');
|
||||
const packageJSON: PackageJSON = JSON.parse(atob(packageJSONData.content));
|
||||
|
||||
assert(packageJSON.name, "name field doesn't exist in package.json");
|
||||
|
||||
const repoUrl = (
|
||||
await octokit.rest.repos.get({
|
||||
owner,
|
||||
repo,
|
||||
})
|
||||
).data.html_url;
|
||||
|
||||
return {
|
||||
repo,
|
||||
packageJSON,
|
||||
repoUrl
|
||||
};
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ const Id = () => {
|
||||
{/* Heading */}
|
||||
<div className="flex flex-col items-center gap-1.5">
|
||||
<Heading as="h3" className="font-medium text-xl">
|
||||
{isAuction? 'Project created successfully.' : 'Project deployed successfully.'}
|
||||
{isAuction? 'Auction created successfully.' : 'Project deployed successfully.'}
|
||||
</Heading>
|
||||
</div>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user