diff --git a/packages/backend/src/registry.ts b/packages/backend/src/registry.ts index 9a473ca3..d678d131 100644 --- a/packages/backend/src/registry.ts +++ b/packages/backend/src/registry.ts @@ -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 { // 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 { + async getDeploymentRecordsByFilter(filter: { [key: string]: any }): Promise { 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 { // 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<{ diff --git a/packages/backend/src/service.ts b/packages/backend/src/service.ts index 0f3a1bbd..22e6144d 100644 --- a/packages/backend/src/service.ts +++ b/packages/backend/src/service.ts @@ -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, - lrn: string + deployerLrn: string ): Promise { 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, - deployer: string + deployerLrn: string ): Promise { 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, + deployerLrn: string, + applicationRecordId: string, + applicationRecordData: ApplicationRecord, + ): Promise { + 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 { diff --git a/packages/backend/src/utils.ts b/packages/backend/src/utils.ts index 83ceb533..86e5147c 100644 --- a/packages/backend/src/utils.ts +++ b/packages/backend/src/utils.ts @@ -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 ( export const sleep = async (ms: number): Promise => 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 + }; +} diff --git a/packages/frontend/src/pages/org-slug/projects/create/success/Id.tsx b/packages/frontend/src/pages/org-slug/projects/create/success/Id.tsx index 9fc32c77..b189d46f 100644 --- a/packages/frontend/src/pages/org-slug/projects/create/success/Id.tsx +++ b/packages/frontend/src/pages/org-slug/projects/create/success/Id.tsx @@ -52,7 +52,7 @@ const Id = () => { {/* Heading */}
- {isAuction? 'Project created successfully.' : 'Project deployed successfully.'} + {isAuction? 'Auction created successfully.' : 'Project deployed successfully.'}