From e816c596ca81ae7b5cab3bb65adda651bd81cb06 Mon Sep 17 00:00:00 2001 From: Nabarun Gogoi Date: Thu, 22 Feb 2024 10:04:33 +0530 Subject: [PATCH] Publish `ApplicationRecord` and `ApplicationDeploymentRequest` on creating new deployments (#89) * Create application deployment request on new deployment * Save application deployment request datas in deployment entity * Add project as dependency * Use project repo name as record app name --------- Co-authored-by: neeraj --- packages/backend/src/entity/Deployment.ts | 15 ++++++ packages/backend/src/entity/Project.ts | 15 ------ packages/backend/src/registry.ts | 25 +++++----- packages/backend/src/service.ts | 49 ++++++++++--------- packages/backend/src/types.ts | 4 +- .../backend/test/fixtures/deployments.json | 22 +++++++++ packages/backend/test/fixtures/projects.json | 10 ---- .../backend/test/publish-deploy-records.ts | 2 +- .../org-slug/projects/id/Deployments.tsx | 4 +- 9 files changed, 78 insertions(+), 68 deletions(-) diff --git a/packages/backend/src/entity/Deployment.ts b/packages/backend/src/entity/Deployment.ts index 62d3f38f..5771b5d2 100644 --- a/packages/backend/src/entity/Deployment.ts +++ b/packages/backend/src/entity/Deployment.ts @@ -26,6 +26,15 @@ export enum DeploymentStatus { Error = 'Error', } +export interface ApplicationDeploymentRequest { + type: string + version: string + name: string + application: string + config: string, + meta: string +} + export interface ApplicationRecord { type: string; version:string @@ -78,6 +87,12 @@ export class Deployment { @Column('simple-json') applicationRecordData!: ApplicationRecord; + @Column('varchar') + applicationDeploymentRequestId!: string; + + @Column('simple-json') + applicationDeploymentRequestData!: ApplicationDeploymentRequest; + @Column('varchar', { nullable: true }) applicationDeploymentRecordId!: string | null; diff --git a/packages/backend/src/entity/Project.ts b/packages/backend/src/entity/Project.ts index 86fa7b3d..4415476d 100644 --- a/packages/backend/src/entity/Project.ts +++ b/packages/backend/src/entity/Project.ts @@ -15,15 +15,6 @@ import { Organization } from './Organization'; import { ProjectMember } from './ProjectMember'; import { Deployment } from './Deployment'; -export interface ApplicationDeploymentRequest { - type: string - version: string - name: string - application: string - config: string, - meta: string -} - @Entity() export class Project { @PrimaryGeneratedColumn('uuid') @@ -52,12 +43,6 @@ export class Project { @Column('varchar', { length: 255, default: 'main' }) prodBranch!: string; - @Column('varchar', { nullable: true }) - applicationDeploymentRequestId!: string | null; - - @Column('simple-json', { nullable: true }) - applicationDeploymentRequestData!: ApplicationDeploymentRequest | null; - @Column('text', { default: '' }) description!: string; diff --git a/packages/backend/src/registry.ts b/packages/backend/src/registry.ts index 108318bb..e3bce569 100644 --- a/packages/backend/src/registry.ts +++ b/packages/backend/src/registry.ts @@ -6,8 +6,7 @@ import { DateTime } from 'luxon'; import { Registry as LaconicRegistry } from '@cerc-io/laconic-sdk'; import { RegistryConfig } from './config'; -import { ApplicationDeploymentRequest } from './entity/Project'; -import { ApplicationRecord, Deployment } from './entity/Deployment'; +import { ApplicationRecord, Deployment, ApplicationDeploymentRequest } from './entity/Deployment'; import { AppDeploymentRecord, PackageJSON } from './types'; const log = debug('snowball:registry'); @@ -27,17 +26,18 @@ export class Registry { } async createApplicationRecord ({ + appName, packageJSON, commitHash, appType, repoUrl }: { + appName: string, packageJSON: PackageJSON commitHash: string, appType: string, repoUrl: string }): Promise<{applicationRecordId: string, applicationRecordData: ApplicationRecord}> { - assert(packageJSON.name, "name field doesn't exist in package.json"); // Use laconic-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 @@ -60,7 +60,7 @@ export class Registry { repository_ref: commitHash, repository: [repoUrl], app_type: appType, - name: packageJSON.name, + name: appName, ...(packageJSON.description && { description: packageJSON.description }), ...(packageJSON.homepage && { homepage: packageJSON.homepage }), ...(packageJSON.license && { license: packageJSON.license }), @@ -81,7 +81,7 @@ export class Registry { log('Application record data:', applicationRecord); // TODO: Discuss computation of CRN - const crn = this.getCrn(packageJSON.name); + const crn = this.getCrn(packageJSON.name, appName); log(`Setting name: ${crn} for record ID: ${result.data.id}`); await this.registry.setName({ cid: result.data.id, crn }, this.registryConfig.privateKey, this.registryConfig.fee); @@ -93,6 +93,7 @@ export class Registry { async createApplicationDeploymentRequest (data: { appName: string, + packageJsonName: string, commitHash: string, repository: string, environmentVariables: { [key: string]: string } @@ -100,7 +101,7 @@ export class Registry { applicationDeploymentRequestId: string, applicationDeploymentRequestData: ApplicationDeploymentRequest }> { - const crn = this.getCrn(data.appName); + const crn = this.getCrn(data.packageJsonName, data.appName); const records = await this.registry.resolveNames([crn]); const applicationRecord = records[0]; @@ -159,14 +160,10 @@ export class Registry { return records.filter((record: AppDeploymentRecord) => deployments.some(deployment => deployment.applicationRecordId === record.attributes.application)); } - getCrn (packageJsonName: string): string { - const [arg1, arg2] = packageJsonName.split('/'); + getCrn (packageJsonName: string, appName: string): string { + const [arg1] = packageJsonName.split('/'); + const authority = arg1.replace('@', ''); - if (arg2) { - const authority = arg1.replace('@', ''); - return `crn://${authority}/applications/${arg2}`; - } - - return `crn://${arg1}/applications/${arg1}`; + return `crn://${authority}/applications/${appName}`; } } diff --git a/packages/backend/src/service.ts b/packages/backend/src/service.ts index 0ce326ff..401e9a18 100644 --- a/packages/backend/src/service.ts +++ b/packages/backend/src/service.ts @@ -15,7 +15,7 @@ import { Permission, ProjectMember } from './entity/ProjectMember'; import { User } from './entity/User'; import { Registry } from './registry'; import { GitHubConfig, RegistryConfig } from './config'; -import { AppDeploymentRecord, GitPushEventPayload } from './types'; +import { AppDeploymentRecord, GitPushEventPayload, PackageJSON } from './types'; const log = debug('snowball:service'); @@ -326,7 +326,9 @@ export class Service { } assert(!Array.isArray(packageJSONData) && packageJSONData.type === 'file'); - const packageJSON = JSON.parse(atob(packageJSONData.content)); + const packageJSON: PackageJSON = JSON.parse(atob(packageJSONData.content)); + + assert(packageJSON.name, "name field doesn't exist in package.json"); if (!recordData.repoUrl) { const { data: repoDetails } = await octokit.rest.repos.get({ owner, repo }); @@ -335,12 +337,30 @@ export class Service { // TODO: Set environment variables for each deployment (environment variables can`t be set in application record) const { applicationRecordId, applicationRecordData } = await this.registry.createApplicationRecord({ + appName: repo, packageJSON, appType: data.project!.template!, commitHash: data.commitHash!, repoUrl: recordData.repoUrl }); + 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 { applicationDeploymentRequestId, applicationDeploymentRequestData } = await this.registry.createApplicationDeploymentRequest( + { + appName: repo, + packageJsonName: packageJSON.name, + commitHash: data.commitHash!, + repository: recordData.repoUrl, + environmentVariables: environmentVariablesObj + }); + // Update previous deployment with prod branch domain // TODO: Fix unique constraint error for domain await this.db.updateDeployment({ @@ -358,6 +378,8 @@ export class Service { status: DeploymentStatus.Building, applicationRecordId, applicationRecordData, + applicationDeploymentRequestId, + applicationDeploymentRequestData, domain: data.domain, createdBy: Object.assign(new User(), { id: userId @@ -393,7 +415,7 @@ export class Service { const { data: repoDetails } = await octokit.rest.repos.get({ owner, repo }); // Create deployment with prod branch and latest commit - const newDeployment = await this.createDeployment(userId, + await this.createDeployment(userId, octokit, { project, @@ -408,27 +430,6 @@ export class Service { } ); - 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 { applicationDeploymentRequestId, applicationDeploymentRequestData } = await this.registry.createApplicationDeploymentRequest( - { - appName: newDeployment.applicationRecordData.name!, - commitHash: latestCommit.sha, - repository: repoDetails.html_url, - environmentVariables: environmentVariablesObj - }); - - await this.db.updateProjectById(project.id, { - applicationDeploymentRequestId, - applicationDeploymentRequestData - }); - await this.createRepoHook(octokit, project); return project; diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index 71626645..e0cef120 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -1,6 +1,6 @@ export interface PackageJSON { - name?: string; - version?: string; + name: string; + version: string; author?: string; description?: string; homepage?: string; diff --git a/packages/backend/test/fixtures/deployments.json b/packages/backend/test/fixtures/deployments.json index 5eb682a8..c0536072 100644 --- a/packages/backend/test/fixtures/deployments.json +++ b/packages/backend/test/fixtures/deployments.json @@ -9,6 +9,8 @@ "isCurrent": true, "applicationRecordId": "qbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", "applicationRecordData": {}, + "applicationDeploymentRequestId": "xqbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", + "applicationDeploymentRequestData": {}, "branch": "main", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitMessage": "subscription added", @@ -24,6 +26,8 @@ "isCurrent": false, "applicationRecordId": "wbafyreihvzya6ovp4yfpkqnddkui2iw7thbhwq74lbqs7bhobvmfhrowoi", "applicationRecordData": {}, + "applicationDeploymentRequestId": "wqbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", + "applicationDeploymentRequestData": {}, "branch": "test", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitMessage": "subscription added", @@ -39,6 +43,8 @@ "isCurrent": false, "applicationRecordId": "ebafyreihvzya6ovp4yfpkqnddkui2iw7t6bhwq74lbqs7bhobvmfhrowoi", "applicationRecordData": {}, + "applicationDeploymentRequestId": "kqbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", + "applicationDeploymentRequestData": {}, "branch": "test", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitMessage": "subscription added", @@ -54,6 +60,8 @@ "isCurrent": false, "applicationRecordId": "rbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhw74lbqs7bhobvmfhrowoi", "applicationRecordData": {}, + "applicationDeploymentRequestId": "yqbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", + "applicationDeploymentRequestData": {}, "branch": "prod", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitMessage": "subscription added", @@ -69,6 +77,8 @@ "isCurrent": true, "applicationRecordId": "tbafyreihvzya6ovp4yfpqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", "applicationRecordData": {}, + "applicationDeploymentRequestId": "pqbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", + "applicationDeploymentRequestData": {}, "branch": "main", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitMessage": "subscription added", @@ -84,6 +94,8 @@ "isCurrent": false, "applicationRecordId": "ybafyreihvzya6ovp4yfpkqnddkui2iw7t6bhwq74lbqs7bhobvmfhrowoi", "applicationRecordData": {}, + "applicationDeploymentRequestId": "tqbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", + "applicationDeploymentRequestData": {}, "branch": "test", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitMessage": "subscription added", @@ -99,6 +111,8 @@ "isCurrent": false, "applicationRecordId": "ubafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvfhrowoi", "applicationRecordData": {}, + "applicationDeploymentRequestId": "eqbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", + "applicationDeploymentRequestData": {}, "branch": "test", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitMessage": "subscription added", @@ -114,6 +128,8 @@ "isCurrent": true, "applicationRecordId": "ibayreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", "applicationRecordData": {}, + "applicationDeploymentRequestId": "dqbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", + "applicationDeploymentRequestData": {}, "branch": "main", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitMessage": "subscription added", @@ -129,6 +145,8 @@ "isCurrent": false, "applicationRecordId": "obafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", "applicationRecordData": {}, + "applicationDeploymentRequestId": "aqbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", + "applicationDeploymentRequestData": {}, "branch": "test", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitMessage": "subscription added", @@ -144,6 +162,8 @@ "isCurrent": false, "applicationRecordId": "pbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowo", "applicationRecordData": {}, + "applicationDeploymentRequestId": "uqbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", + "applicationDeploymentRequestData": {}, "branch": "test", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitMessage": "subscription added", @@ -159,6 +179,8 @@ "isCurrent": true, "applicationRecordId": "pbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowo", "applicationRecordData": {}, + "applicationDeploymentRequestId": "pqbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", + "applicationDeploymentRequestData": {}, "branch": "test", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitMessage": "subscription added", diff --git a/packages/backend/test/fixtures/projects.json b/packages/backend/test/fixtures/projects.json index 55c5ee23..f4564643 100644 --- a/packages/backend/test/fixtures/projects.json +++ b/packages/backend/test/fixtures/projects.json @@ -10,8 +10,6 @@ "framework": "test", "webhooks": [], "icon": "", - "applicationDeploymentRequestId": "hbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", - "applicationDeploymentRequestData": {}, "subDomain": "testProject.snowball.xyz" }, { @@ -25,8 +23,6 @@ "framework": "test-2", "webhooks": [], "icon": "", - "applicationDeploymentRequestId": "gbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", - "applicationDeploymentRequestData": {}, "subDomain": "testProject-2.snowball.xyz" }, { @@ -40,8 +36,6 @@ "framework": "test-3", "webhooks": [], "icon": "", - "applicationDeploymentRequestId": "ebafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", - "applicationDeploymentRequestData": {}, "subDomain": "iglootools.snowball.xyz" }, { @@ -55,8 +49,6 @@ "framework": "test-4", "webhooks": [], "icon": "", - "applicationDeploymentRequestId": "qbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", - "applicationDeploymentRequestData": {}, "subDomain": "iglootools-2.snowball.xyz" }, { @@ -70,8 +62,6 @@ "framework": "test-5", "webhooks": [], "icon": "", - "applicationDeploymentRequestId": "xbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", - "applicationDeploymentRequestData": {}, "subDomain": "snowball-2.snowball.xyz" } ] diff --git a/packages/backend/test/publish-deploy-records.ts b/packages/backend/test/publish-deploy-records.ts index f5636c4e..90ccc16e 100644 --- a/packages/backend/test/publish-deploy-records.ts +++ b/packages/backend/test/publish-deploy-records.ts @@ -53,7 +53,7 @@ async function main () { so: '66fcfa49a1664d4cb4ce4f72c1c0e151' }), - request: deployment.project.applicationDeploymentRequestId, + request: deployment.applicationDeploymentRequestId, url }; diff --git a/packages/frontend/src/pages/org-slug/projects/id/Deployments.tsx b/packages/frontend/src/pages/org-slug/projects/id/Deployments.tsx index 7049a270..1d9e4e2d 100644 --- a/packages/frontend/src/pages/org-slug/projects/id/Deployments.tsx +++ b/packages/frontend/src/pages/org-slug/projects/id/Deployments.tsx @@ -30,14 +30,14 @@ const DeploymentsTabPanel = () => { const fetchDeployments = useCallback(async () => { const { deployments } = await client.getDeployments(project.id); setDeployments(deployments); - }, [client]); + }, [client, project]); const fetchProductionBranchDomains = useCallback(async () => { const { domains } = await client.getDomains(project.id, { branch: project.prodBranch, }); setProdBranchDomains(domains); - }, [client]); + }, [client, project]); useEffect(() => { fetchProductionBranchDomains();