forked from cerc-io/snowballtools-base
		
	Check for ApplicationDeploymentRecord and update deployments (#73)
				
					
				
			* Add skeleton and TODOs for polling deployment records * Add method implementations for fetching deployment records * Handle if deployment url is not set * Add logs after getting ApplicationDeploymentRecord * Add application deployment record to deployment entity * Change type of application deployment record data * Fetch delay to check deployment records from config * Update isCurrent after deployment record received --------- Co-authored-by: neeraj <neeraj.rtly@gmail.com>
This commit is contained in:
		
							parent
							
								
									b007286f4a
								
							
						
					
					
						commit
						ce55fe62d8
					
				| @ -31,7 +31,7 @@ | ||||
| - Load fixtures in database | ||||
| 
 | ||||
|   ```bash | ||||
|   yarn db:load:fixtures | ||||
|   yarn test:db:load:fixtures | ||||
|   ``` | ||||
| 
 | ||||
| - Set `gitHub.oAuth.clientId` and `gitHub.oAuth.clientSecret` in backend [config file](packages/backend/environments/local.toml) | ||||
| @ -66,7 +66,7 @@ | ||||
| - Run the script to create bond, reserve the authority and set authority bond | ||||
| 
 | ||||
|   ```bash | ||||
|   yarn registry:init | ||||
|   yarn test:registry:init | ||||
|   # snowball:initialize-registry bondId: 6af0ab81973b93d3511ae79841756fb5da3fd2f70ea1279e81fae7c9b19af6c4 +0ms | ||||
|   ``` | ||||
| 
 | ||||
|  | ||||
| @ -13,6 +13,7 @@ | ||||
|     clientSecret = "" | ||||
| 
 | ||||
| [registryConfig] | ||||
|   fetchDeploymentRecordDelay = 5000 | ||||
|   restEndpoint = "http://localhost:1317" | ||||
|   gqlEndpoint = "http://localhost:9473/api" | ||||
|   chainId = "laconic_9000-1" | ||||
|  | ||||
| @ -36,10 +36,10 @@ | ||||
|     "lint": "eslint .", | ||||
|     "format": "prettier --write .", | ||||
|     "format:check": "prettier --check .", | ||||
|     "registry:init": "DEBUG=snowball:* ts-node ./test/initialize-registry.ts", | ||||
|     "registry:publish-deploy-records": "DEBUG=snowball:* ts-node ./test/publish-deploy-records.ts", | ||||
|     "db:load:fixtures": "DEBUG=snowball:* ts-node ./test/initialize-db.ts", | ||||
|     "db:delete": "DEBUG=snowball:* ts-node ./test/delete-db.ts" | ||||
|     "test:registry:init": "DEBUG=snowball:* ts-node ./test/initialize-registry.ts", | ||||
|     "test:registry:publish-deploy-records": "DEBUG=snowball:* ts-node ./test/publish-deploy-records.ts", | ||||
|     "test:db:load:fixtures": "DEBUG=snowball:* ts-node ./test/initialize-db.ts", | ||||
|     "test:db:delete": "DEBUG=snowball:* ts-node ./test/delete-db.ts" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@types/fs-extra": "^11.0.4", | ||||
|  | ||||
| @ -22,6 +22,7 @@ export interface RegistryConfig { | ||||
|   chainId: string; | ||||
|   privateKey: string; | ||||
|   bondId: string; | ||||
|   fetchDeploymentRecordDelay: number; | ||||
|   fee: { | ||||
|     amount: string; | ||||
|     denom: string; | ||||
|  | ||||
| @ -126,8 +126,18 @@ export class Database { | ||||
|     return projects; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Get deployments with specified filter | ||||
|    */ | ||||
|   async getDeployments (options: FindManyOptions<Deployment>): Promise<Deployment[]> { | ||||
|     const deploymentRepository = this.dataSource.getRepository(Deployment); | ||||
|     const deployments = await deploymentRepository.find(options); | ||||
| 
 | ||||
|     return deployments; | ||||
|   } | ||||
| 
 | ||||
|   async getDeploymentsByProjectId (projectId: string): Promise<Deployment[]> { | ||||
|     const deployments = await this.getDeployments({ | ||||
|     return this.getDeployments({ | ||||
|       relations: { | ||||
|         project: true, | ||||
|         domain: true, | ||||
| @ -142,15 +152,6 @@ export class Database { | ||||
|         createdAt: 'DESC' | ||||
|       } | ||||
|     }); | ||||
| 
 | ||||
|     return deployments; | ||||
|   } | ||||
| 
 | ||||
|   async getDeployments (options: FindManyOptions<Deployment>): Promise<Deployment[]> { | ||||
|     const deploymentRepository = this.dataSource.getRepository(Deployment); | ||||
|     const deployments = await deploymentRepository.find(options); | ||||
| 
 | ||||
|     return deployments; | ||||
|   } | ||||
| 
 | ||||
|   async getDeployment (options: FindOneOptions<Deployment>): Promise<Deployment | null> { | ||||
| @ -167,7 +168,7 @@ export class Database { | ||||
|     return domains; | ||||
|   } | ||||
| 
 | ||||
|   async addDeployement (data: DeepPartial<Deployment>): Promise<Deployment> { | ||||
|   async addDeployment (data: DeepPartial<Deployment>): Promise<Deployment> { | ||||
|     const deploymentRepository = this.dataSource.getRepository(Deployment); | ||||
| 
 | ||||
|     const id = nanoid(); | ||||
|  | ||||
| @ -12,6 +12,7 @@ import { | ||||
| import { Project } from './Project'; | ||||
| import { Domain } from './Domain'; | ||||
| import { User } from './User'; | ||||
| import { AppDeploymentRecordAttributes } from '../types'; | ||||
| 
 | ||||
| export enum Environment { | ||||
|   Production = 'Production', | ||||
| @ -45,6 +46,9 @@ export class Deployment { | ||||
|   @PrimaryColumn('varchar') | ||||
|     id!: string; | ||||
| 
 | ||||
|   @Column() | ||||
|     projectId!: string; | ||||
| 
 | ||||
|   @ManyToOne(() => Project, { onDelete: 'CASCADE' }) | ||||
|   @JoinColumn({ name: 'projectId' }) | ||||
|     project!: Project; | ||||
| @ -69,10 +73,16 @@ export class Deployment { | ||||
|     url!: string | null; | ||||
| 
 | ||||
|   @Column('varchar') | ||||
|     registryRecordId!: string; | ||||
|     applicationRecordId!: string; | ||||
| 
 | ||||
|   @Column('simple-json') | ||||
|     registryRecordData!: ApplicationRecord; | ||||
|     applicationRecordData!: ApplicationRecord; | ||||
| 
 | ||||
|   @Column('varchar', { nullable: true }) | ||||
|     applicationDeploymentRecordId!: string | null; | ||||
| 
 | ||||
|   @Column('simple-json', { nullable: true }) | ||||
|     applicationDeploymentRecordData!: AppDeploymentRecordAttributes | null; | ||||
| 
 | ||||
|   @Column({ | ||||
|     enum: Environment | ||||
|  | ||||
| @ -53,10 +53,10 @@ export class Project { | ||||
|     prodBranch!: string; | ||||
| 
 | ||||
|   @Column('varchar', { nullable: true }) | ||||
|     registryRecordId!: string | null; | ||||
|     applicationDeploymentRequestId!: string | null; | ||||
| 
 | ||||
|   @Column('simple-json', { nullable: true }) | ||||
|     registryRecordData!: ApplicationDeploymentRequest | null; | ||||
|     applicationDeploymentRequestData!: ApplicationDeploymentRequest | null; | ||||
| 
 | ||||
|   @Column('text', { default: '' }) | ||||
|     description!: string; | ||||
|  | ||||
| @ -31,7 +31,7 @@ export const main = async (): Promise<void> => { | ||||
|   await db.init(); | ||||
| 
 | ||||
|   const registry = new Registry(registryConfig); | ||||
|   const service = new Service({ gitHubConfig: gitHub }, db, app, registry); | ||||
|   const service = new Service({ gitHubConfig: gitHub, registryConfig }, db, app, registry); | ||||
| 
 | ||||
|   const typeDefs = fs.readFileSync(path.join(__dirname, 'schema.gql')).toString(); | ||||
|   const resolvers = await createResolvers(service); | ||||
|  | ||||
| @ -7,13 +7,14 @@ import { Registry as LaconicRegistry } from '@cerc-io/laconic-sdk'; | ||||
| 
 | ||||
| import { RegistryConfig } from './config'; | ||||
| import { ApplicationDeploymentRequest } from './entity/Project'; | ||||
| import { ApplicationRecord } from './entity/Deployment'; | ||||
| import { PackageJSON } from './types'; | ||||
| import { ApplicationRecord, Deployment } from './entity/Deployment'; | ||||
| import { AppDeploymentRecord, PackageJSON } from './types'; | ||||
| 
 | ||||
| const log = debug('snowball:registry'); | ||||
| 
 | ||||
| const APP_RECORD_TYPE = 'ApplicationRecord'; | ||||
| const DEPLOYMENT_RECORD_TYPE = 'ApplicationDeploymentRequest'; | ||||
| const APP_DEPLOYMENT_REQUEST_TYPE = 'ApplicationDeploymentRequest'; | ||||
| const APP_DEPLOYMENT_RECORD_TYPE = 'ApplicationDeploymentRecord'; | ||||
| 
 | ||||
| // TODO: Move registry code to laconic-sdk/watcher-ts
 | ||||
| export class Registry { | ||||
| @ -35,7 +36,7 @@ export class Registry { | ||||
|     commitHash: string, | ||||
|     appType: string, | ||||
|     repoUrl: string | ||||
|   }): Promise<{registryRecordId: string, registryRecordData: ApplicationRecord}> { | ||||
|   }): 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
 | ||||
| @ -87,7 +88,7 @@ export class Registry { | ||||
|     await this.registry.setName({ cid: result.data.id, crn: `${crn}@${applicationRecord.app_version}` }, this.registryConfig.privateKey, this.registryConfig.fee); | ||||
|     await this.registry.setName({ cid: result.data.id, crn: `${crn}@${applicationRecord.repository_ref}` }, this.registryConfig.privateKey, this.registryConfig.fee); | ||||
| 
 | ||||
|     return { registryRecordId: result.data.id, registryRecordData: applicationRecord }; | ||||
|     return { applicationRecordId: result.data.id, applicationRecordData: applicationRecord }; | ||||
|   } | ||||
| 
 | ||||
|   async createApplicationDeploymentRequest (data: { | ||||
| @ -96,8 +97,8 @@ export class Registry { | ||||
|     repository: string, | ||||
|     environmentVariables: { [key: string]: string } | ||||
|   }): Promise<{ | ||||
|     registryRecordId: string, | ||||
|     registryRecordData: ApplicationDeploymentRequest | ||||
|     applicationDeploymentRequestId: string, | ||||
|     applicationDeploymentRequestData: ApplicationDeploymentRequest | ||||
|   }> { | ||||
|     const crn = this.getCrn(data.appName); | ||||
|     const records = await this.registry.resolveNames([crn]); | ||||
| @ -109,7 +110,7 @@ export class Registry { | ||||
| 
 | ||||
|     // Create record of type ApplicationDeploymentRequest and publish
 | ||||
|     const applicationDeploymentRequest = { | ||||
|       type: DEPLOYMENT_RECORD_TYPE, | ||||
|       type: APP_DEPLOYMENT_REQUEST_TYPE, | ||||
|       version: '1.0.0', | ||||
|       name: `${applicationRecord.attributes.name}@${applicationRecord.attributes.app_version}`, | ||||
|       application: `${crn}@${applicationRecord.attributes.app_version}`, | ||||
| @ -141,7 +142,21 @@ export class Registry { | ||||
|     log(`Application deployment request record published: ${result.data.id}`); | ||||
|     log('Application deployment request data:', applicationDeploymentRequest); | ||||
| 
 | ||||
|     return { registryRecordId: result.data.id, registryRecordData: applicationDeploymentRequest }; | ||||
|     return { applicationDeploymentRequestId: result.data.id, applicationDeploymentRequestData: applicationDeploymentRequest }; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Fetch ApplicationDeploymentRecords for deployments | ||||
|    */ | ||||
|   async getDeploymentRecords (deployments: Deployment[]): Promise<AppDeploymentRecord[]> { | ||||
|     // Fetch ApplicationDeploymentRecords for corresponding ApplicationRecord set in deployments
 | ||||
|     // TODO: Implement Laconicd GQL query to filter records by multiple values for an attribute
 | ||||
|     const records = await this.registry.queryRecords({ | ||||
|       type: APP_DEPLOYMENT_RECORD_TYPE | ||||
|     }, true); | ||||
| 
 | ||||
|     // Filter records with ApplicationRecord ids
 | ||||
|     return records.filter((record: AppDeploymentRecord) => deployments.some(deployment => deployment.applicationRecordId === record.attributes.application)); | ||||
|   } | ||||
| 
 | ||||
|   getCrn (packageJsonName: string): string { | ||||
|  | ||||
| @ -30,7 +30,7 @@ export const createResolvers = async (service: Service): Promise<any> => { | ||||
|       }, | ||||
| 
 | ||||
|       deployments: async (_: any, { projectId }: { projectId: string }) => { | ||||
|         return service.getDeployementsByProjectId(projectId); | ||||
|         return service.getDeploymentsByProjectId(projectId); | ||||
|       }, | ||||
| 
 | ||||
|       environmentVariables: async (_: any, { projectId }: { projectId: string }) => { | ||||
|  | ||||
| @ -91,7 +91,7 @@ type Deployment { | ||||
|   branch: String! | ||||
|   commitHash: String! | ||||
|   commitMessage: String! | ||||
|   url: String! | ||||
|   url: String | ||||
|   environment: Environment! | ||||
|   isCurrent: Boolean! | ||||
|   status: DeploymentStatus! | ||||
|  | ||||
| @ -14,14 +14,16 @@ import { Project } from './entity/Project'; | ||||
| import { Permission, ProjectMember } from './entity/ProjectMember'; | ||||
| import { User } from './entity/User'; | ||||
| import { Registry } from './registry'; | ||||
| import { GitHubConfig } from './config'; | ||||
| import { GitPushEventPayload } from './types'; | ||||
| import { GitHubConfig, RegistryConfig } from './config'; | ||||
| import { AppDeploymentRecord, GitPushEventPayload } from './types'; | ||||
| 
 | ||||
| const log = debug('snowball:service'); | ||||
| 
 | ||||
| const GITHUB_UNIQUE_WEBHOOK_ERROR = 'Hook already exists on this repository'; | ||||
| 
 | ||||
| interface Config { | ||||
|   gitHubConfig: GitHubConfig | ||||
|   registryConfig: RegistryConfig | ||||
| } | ||||
| 
 | ||||
| export class Service { | ||||
| @ -30,11 +32,113 @@ export class Service { | ||||
|   private registry: Registry; | ||||
|   private config: Config; | ||||
| 
 | ||||
|   private deployRecordCheckTimeout?: NodeJS.Timeout; | ||||
| 
 | ||||
|   constructor (config: Config, db: Database, app: OAuthApp, registry: Registry) { | ||||
|     this.db = db; | ||||
|     this.oauthApp = app; | ||||
|     this.registry = registry; | ||||
|     this.config = config; | ||||
|     this.init(); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Initialize services | ||||
|    */ | ||||
|   init (): void { | ||||
|     // Start check for ApplicationDeploymentRecords asynchronously
 | ||||
|     this.checkDeployRecordsAndUpdate(); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Destroy services | ||||
|    */ | ||||
|   destroy (): void { | ||||
|     clearTimeout(this.deployRecordCheckTimeout); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Checks for ApplicationDeploymentRecord and update corresponding deployments | ||||
|    * Continues check in loop after a delay of DEPLOY_RECORD_CHECK_DELAY_MS | ||||
|    */ | ||||
|   async checkDeployRecordsAndUpdate (): Promise<void> { | ||||
|     // Fetch deployments in building state
 | ||||
|     const deployments = await this.db.getDeployments({ | ||||
|       where: { | ||||
|         status: DeploymentStatus.Building | ||||
|         // TODO: Fetch and check records for recent deployments
 | ||||
|       } | ||||
|     }); | ||||
| 
 | ||||
|     if (deployments.length) { | ||||
|       log(`Found ${deployments.length} deployments in ${DeploymentStatus.Building} state`); | ||||
| 
 | ||||
|       // Fetch ApplicationDeploymentRecord for deployments
 | ||||
|       const records = await this.registry.getDeploymentRecords(deployments); | ||||
|       log(`Found ${records.length} ApplicationDeploymentRecords`); | ||||
| 
 | ||||
|       // Update deployments for which ApplicationDeploymentRecords were returned
 | ||||
|       await this.updateDeploymentsWithRecordData(records); | ||||
|     } | ||||
| 
 | ||||
|     this.deployRecordCheckTimeout = setTimeout(() => { | ||||
|       this.checkDeployRecordsAndUpdate(); | ||||
|     }, this.config.registryConfig.fetchDeploymentRecordDelay); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Update deployments with ApplicationDeploymentRecord data | ||||
|    */ | ||||
|   async updateDeploymentsWithRecordData (records: AppDeploymentRecord[]): Promise<void> { | ||||
|     // Get deployments for ApplicationDeploymentRecords
 | ||||
|     const deployments = await this.db.getDeployments({ | ||||
|       where: records.map(record => ({ | ||||
|         applicationRecordId: record.attributes.application | ||||
|       })), | ||||
|       order: { | ||||
|         createdAt: 'DESC' | ||||
|       } | ||||
|     }); | ||||
| 
 | ||||
|     // Get project IDs of deployments that are in production environment
 | ||||
|     const productionDeploymentProjectIds = deployments.reduce((acc, deployment): Set<string> => { | ||||
|       if (deployment.environment === Environment.Production) { | ||||
|         acc.add(deployment.projectId); | ||||
|       } | ||||
| 
 | ||||
|       return acc; | ||||
|     }, new Set<string>()); | ||||
| 
 | ||||
|     // Set old deployments isCurrent to false
 | ||||
|     await Promise.all(Array.from(productionDeploymentProjectIds).map( | ||||
|       // TODO: Add DB method to update multiple deployments in single query
 | ||||
|       async (projectId) => this.db.updateDeployment({ projectId }, { isCurrent: false }) | ||||
|     )); | ||||
| 
 | ||||
|     const recordToDeploymentsMap = deployments.reduce((acc: {[key: string]: Deployment}, deployment) => { | ||||
|       acc[deployment.applicationRecordId] = deployment; | ||||
|       return acc; | ||||
|     }, {}); | ||||
| 
 | ||||
|     // Update deployment data for ApplicationDeploymentRecords
 | ||||
|     const deploymentUpdatePromises = records.map(async (record) => { | ||||
|       const deployment = recordToDeploymentsMap[record.attributes.application]; | ||||
| 
 | ||||
|       await this.db.updateDeploymentById( | ||||
|         deployment.id, | ||||
|         { | ||||
|           applicationDeploymentRecordId: record.id, | ||||
|           applicationDeploymentRecordData: record.attributes, | ||||
|           url: record.attributes.url, | ||||
|           status: DeploymentStatus.Ready, | ||||
|           isCurrent: deployment.environment === Environment.Production | ||||
|         } | ||||
|       ); | ||||
| 
 | ||||
|       log(`Updated deployment deployment ${deployment.id} with URL ${record.attributes.url}`); | ||||
|     }); | ||||
| 
 | ||||
|     await Promise.all(deploymentUpdatePromises); | ||||
|   } | ||||
| 
 | ||||
|   async getUser (userId: string): Promise<User | null> { | ||||
| @ -67,7 +171,7 @@ export class Service { | ||||
|     return dbProjects; | ||||
|   } | ||||
| 
 | ||||
|   async getDeployementsByProjectId (projectId: string): Promise<Deployment[]> { | ||||
|   async getDeploymentsByProjectId (projectId: string): Promise<Deployment[]> { | ||||
|     const dbDeployments = await this.db.getDeploymentsByProjectId(projectId); | ||||
|     return dbDeployments; | ||||
|   } | ||||
| @ -187,7 +291,7 @@ export class Service { | ||||
| 
 | ||||
|     const octokit = await this.getOctokit(userId); | ||||
| 
 | ||||
|     const newDeployement = await this.createDeployment(userId, | ||||
|     const newDeployment = await this.createDeployment(userId, | ||||
|       octokit, | ||||
|       { | ||||
|         project: oldDeployment.project, | ||||
| @ -199,7 +303,7 @@ export class Service { | ||||
|         commitMessage: oldDeployment.commitMessage | ||||
|       }); | ||||
| 
 | ||||
|     return newDeployement; | ||||
|     return newDeployment; | ||||
|   } | ||||
| 
 | ||||
|   async createDeployment ( | ||||
| @ -232,24 +336,13 @@ export class Service { | ||||
|     } | ||||
| 
 | ||||
|     // TODO: Set environment variables for each deployment (environment variables can`t be set in application record)
 | ||||
|     const { registryRecordId, registryRecordData } = await this.registry.createApplicationRecord({ | ||||
|     const { applicationRecordId, applicationRecordData } = await this.registry.createApplicationRecord({ | ||||
|       packageJSON, | ||||
|       appType: data.project!.template!, | ||||
|       commitHash: data.commitHash!, | ||||
|       repoUrl: recordData.repoUrl | ||||
|     }); | ||||
| 
 | ||||
|     // Check if new deployment is set to current
 | ||||
|     if (data.isCurrent) { | ||||
|       // Update previous current deployment
 | ||||
|       await this.db.updateDeployment({ | ||||
|         project: { | ||||
|           id: data.project.id | ||||
|         }, | ||||
|         isCurrent: true | ||||
|       }, { isCurrent: false }); | ||||
|     } | ||||
| 
 | ||||
|     // Update previous deployment with prod branch domain
 | ||||
|     // TODO: Fix unique constraint error for domain
 | ||||
|     await this.db.updateDeployment({ | ||||
| @ -258,7 +351,7 @@ export class Service { | ||||
|       domain: null | ||||
|     }); | ||||
| 
 | ||||
|     const newDeployement = await this.db.addDeployement({ | ||||
|     const newDeployment = await this.db.addDeployment({ | ||||
|       project: data.project, | ||||
|       branch: data.branch, | ||||
|       commitHash: data.commitHash, | ||||
| @ -266,16 +359,16 @@ export class Service { | ||||
|       environment: data.environment, | ||||
|       isCurrent: data.isCurrent, | ||||
|       status: DeploymentStatus.Building, | ||||
|       registryRecordId, | ||||
|       registryRecordData, | ||||
|       applicationRecordId, | ||||
|       applicationRecordData, | ||||
|       domain: data.domain, | ||||
|       createdBy: Object.assign(new User(), { | ||||
|         id: userId | ||||
|       }) | ||||
|     }); | ||||
| 
 | ||||
|     log(`Created deployment ${newDeployement.id} and published application record ${registryRecordId}`); | ||||
|     return newDeployement; | ||||
|     log(`Created deployment ${newDeployment.id} and published application record ${applicationRecordId}`); | ||||
|     return newDeployment; | ||||
|   } | ||||
| 
 | ||||
|   async addProject (userId: string, organizationSlug: string, data: DeepPartial<Project>): Promise<Project | undefined> { | ||||
| @ -327,17 +420,17 @@ export class Service { | ||||
|       return acc; | ||||
|     }, {} as { [key: string]: string }); | ||||
| 
 | ||||
|     const { registryRecordId, registryRecordData } = await this.registry.createApplicationDeploymentRequest( | ||||
|     const { applicationDeploymentRequestId, applicationDeploymentRequestData } = await this.registry.createApplicationDeploymentRequest( | ||||
|       { | ||||
|         appName: newDeployment.registryRecordData.name!, | ||||
|         appName: newDeployment.applicationRecordData.name!, | ||||
|         commitHash: latestCommit.sha, | ||||
|         repository: repoDetails.html_url, | ||||
|         environmentVariables: environmentVariablesObj | ||||
|       }); | ||||
| 
 | ||||
|     await this.db.updateProjectById(project.id, { | ||||
|       registryRecordId, | ||||
|       registryRecordData | ||||
|       applicationDeploymentRequestId, | ||||
|       applicationDeploymentRequestData | ||||
|     }); | ||||
| 
 | ||||
|     await this.createRepoHook(octokit, project); | ||||
| @ -444,7 +537,7 @@ export class Service { | ||||
| 
 | ||||
|     const octokit = await this.getOctokit(userId); | ||||
| 
 | ||||
|     const newDeployement = await this.createDeployment(userId, | ||||
|     const newDeployment = await this.createDeployment(userId, | ||||
|       octokit, | ||||
|       { | ||||
|         project: oldDeployment.project, | ||||
| @ -457,7 +550,7 @@ export class Service { | ||||
|         commitMessage: oldDeployment.commitMessage | ||||
|       }); | ||||
| 
 | ||||
|     return newDeployement; | ||||
|     return newDeployment; | ||||
|   } | ||||
| 
 | ||||
|   async rollbackDeployment (projectId: string, deploymentId: string): Promise<boolean> { | ||||
| @ -475,7 +568,7 @@ export class Service { | ||||
|     }); | ||||
| 
 | ||||
|     if (!oldCurrentDeployment) { | ||||
|       throw new Error('Current deployement doesnot exist'); | ||||
|       throw new Error('Current deployment doesnot exist'); | ||||
|     } | ||||
| 
 | ||||
|     const oldCurrentDeploymentUpdate = await this.db.updateDeploymentById(oldCurrentDeployment.id, { isCurrent: false, domain: null }); | ||||
|  | ||||
| @ -25,3 +25,27 @@ export interface GitPushEventPayload { | ||||
|     message: string; | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| export interface AppDeploymentRecordAttributes { | ||||
|   application: string; | ||||
|   dns: string; | ||||
|   meta: string; | ||||
|   name: string; | ||||
|   request: string; | ||||
|   type: string; | ||||
|   url: string; | ||||
|   version: string; | ||||
| } | ||||
| 
 | ||||
| interface RegistryRecord { | ||||
|   id: string; | ||||
|   names: string[] | null; | ||||
|   owners: string[]; | ||||
|   bondId: string; | ||||
|   createTime: string; | ||||
|   expiryTime: string; | ||||
| } | ||||
| 
 | ||||
| export interface AppDeploymentRecord extends RegistryRecord { | ||||
|   attributes: AppDeploymentRecordAttributes | ||||
| } | ||||
|  | ||||
							
								
								
									
										44
									
								
								packages/backend/test/fixtures/deployments.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										44
									
								
								packages/backend/test/fixtures/deployments.json
									
									
									
									
										vendored
									
									
								
							| @ -7,8 +7,8 @@ | ||||
|     "status": "Ready", | ||||
|     "environment": "Production", | ||||
|     "isCurrent": true, | ||||
|     "registryRecordId": "qbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", | ||||
|     "registryRecordData": {}, | ||||
|     "applicationRecordId": "qbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", | ||||
|     "applicationRecordData": {}, | ||||
|     "branch": "main", | ||||
|     "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", | ||||
|     "commitMessage": "subscription added", | ||||
| @ -22,8 +22,8 @@ | ||||
|     "status": "Ready", | ||||
|     "environment": "Preview", | ||||
|     "isCurrent": false, | ||||
|     "registryRecordId": "wbafyreihvzya6ovp4yfpkqnddkui2iw7thbhwq74lbqs7bhobvmfhrowoi", | ||||
|     "registryRecordData": {}, | ||||
|     "applicationRecordId": "wbafyreihvzya6ovp4yfpkqnddkui2iw7thbhwq74lbqs7bhobvmfhrowoi", | ||||
|     "applicationRecordData": {}, | ||||
|     "branch": "test", | ||||
|     "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", | ||||
|     "commitMessage": "subscription added", | ||||
| @ -37,8 +37,8 @@ | ||||
|     "status": "Ready", | ||||
|     "environment": "Development", | ||||
|     "isCurrent": false, | ||||
|     "registryRecordId": "ebafyreihvzya6ovp4yfpkqnddkui2iw7t6bhwq74lbqs7bhobvmfhrowoi", | ||||
|     "registryRecordData": {}, | ||||
|     "applicationRecordId": "ebafyreihvzya6ovp4yfpkqnddkui2iw7t6bhwq74lbqs7bhobvmfhrowoi", | ||||
|     "applicationRecordData": {}, | ||||
|     "branch": "test", | ||||
|     "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", | ||||
|     "commitMessage": "subscription added", | ||||
| @ -52,8 +52,8 @@ | ||||
|     "status": "Ready", | ||||
|     "environment": "Production", | ||||
|     "isCurrent": false, | ||||
|     "registryRecordId": "rbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhw74lbqs7bhobvmfhrowoi", | ||||
|     "registryRecordData": {}, | ||||
|     "applicationRecordId": "rbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhw74lbqs7bhobvmfhrowoi", | ||||
|     "applicationRecordData": {}, | ||||
|     "branch": "prod", | ||||
|     "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", | ||||
|     "commitMessage": "subscription added", | ||||
| @ -67,8 +67,8 @@ | ||||
|     "status": "Ready", | ||||
|     "environment": "Production", | ||||
|     "isCurrent": true, | ||||
|     "registryRecordId": "tbafyreihvzya6ovp4yfpqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", | ||||
|     "registryRecordData": {}, | ||||
|     "applicationRecordId": "tbafyreihvzya6ovp4yfpqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", | ||||
|     "applicationRecordData": {}, | ||||
|     "branch": "main", | ||||
|     "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", | ||||
|     "commitMessage": "subscription added", | ||||
| @ -82,8 +82,8 @@ | ||||
|     "status": "Ready", | ||||
|     "environment": "Preview", | ||||
|     "isCurrent": false, | ||||
|     "registryRecordId": "ybafyreihvzya6ovp4yfpkqnddkui2iw7t6bhwq74lbqs7bhobvmfhrowoi", | ||||
|     "registryRecordData": {}, | ||||
|     "applicationRecordId": "ybafyreihvzya6ovp4yfpkqnddkui2iw7t6bhwq74lbqs7bhobvmfhrowoi", | ||||
|     "applicationRecordData": {}, | ||||
|     "branch": "test", | ||||
|     "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", | ||||
|     "commitMessage": "subscription added", | ||||
| @ -97,8 +97,8 @@ | ||||
|     "status": "Ready", | ||||
|     "environment": "Development", | ||||
|     "isCurrent": false, | ||||
|     "registryRecordId": "ubafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvfhrowoi", | ||||
|     "registryRecordData": {}, | ||||
|     "applicationRecordId": "ubafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvfhrowoi", | ||||
|     "applicationRecordData": {}, | ||||
|     "branch": "test", | ||||
|     "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", | ||||
|     "commitMessage": "subscription added", | ||||
| @ -112,8 +112,8 @@ | ||||
|     "status": "Ready", | ||||
|     "environment": "Production", | ||||
|     "isCurrent": true, | ||||
|     "registryRecordId": "ibayreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", | ||||
|     "registryRecordData": {}, | ||||
|     "applicationRecordId": "ibayreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", | ||||
|     "applicationRecordData": {}, | ||||
|     "branch": "main", | ||||
|     "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", | ||||
|     "commitMessage": "subscription added", | ||||
| @ -127,8 +127,8 @@ | ||||
|     "status": "Ready", | ||||
|     "environment": "Preview", | ||||
|     "isCurrent": false, | ||||
|     "registryRecordId": "obafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", | ||||
|     "registryRecordData": {}, | ||||
|     "applicationRecordId": "obafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", | ||||
|     "applicationRecordData": {}, | ||||
|     "branch": "test", | ||||
|     "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", | ||||
|     "commitMessage": "subscription added", | ||||
| @ -142,8 +142,8 @@ | ||||
|     "status": "Ready", | ||||
|     "environment": "Development", | ||||
|     "isCurrent": false, | ||||
|     "registryRecordId": "pbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowo", | ||||
|     "registryRecordData": {}, | ||||
|     "applicationRecordId": "pbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowo", | ||||
|     "applicationRecordData": {}, | ||||
|     "branch": "test", | ||||
|     "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", | ||||
|     "commitMessage": "subscription added", | ||||
| @ -157,8 +157,8 @@ | ||||
|     "status": "Ready", | ||||
|     "environment": "Production", | ||||
|     "isCurrent": true, | ||||
|     "registryRecordId": "pbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowo", | ||||
|     "registryRecordData": {}, | ||||
|     "applicationRecordId": "pbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowo", | ||||
|     "applicationRecordData": {}, | ||||
|     "branch": "test", | ||||
|     "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", | ||||
|     "commitMessage": "subscription added", | ||||
|  | ||||
							
								
								
									
										20
									
								
								packages/backend/test/fixtures/projects.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										20
									
								
								packages/backend/test/fixtures/projects.json
									
									
									
									
										vendored
									
									
								
							| @ -10,8 +10,8 @@ | ||||
|     "framework": "test", | ||||
|     "webhooks": [], | ||||
|     "icon": "", | ||||
|     "registryRecordId": "hbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", | ||||
|     "registryRecordData": {}, | ||||
|     "applicationDeploymentRequestId": "hbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", | ||||
|     "applicationDeploymentRequestData": {}, | ||||
|     "subDomain": "testProject.snowball.xyz" | ||||
|   }, | ||||
|   { | ||||
| @ -25,8 +25,8 @@ | ||||
|     "framework": "test-2", | ||||
|     "webhooks": [], | ||||
|     "icon": "", | ||||
|     "registryRecordId": "gbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", | ||||
|     "registryRecordData": {}, | ||||
|     "applicationDeploymentRequestId": "gbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", | ||||
|     "applicationDeploymentRequestData": {}, | ||||
|     "subDomain": "testProject-2.snowball.xyz" | ||||
|   }, | ||||
|   { | ||||
| @ -40,8 +40,8 @@ | ||||
|     "framework": "test-3", | ||||
|     "webhooks": [], | ||||
|     "icon": "", | ||||
|     "registryRecordId": "ebafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", | ||||
|     "registryRecordData": {}, | ||||
|     "applicationDeploymentRequestId": "ebafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", | ||||
|     "applicationDeploymentRequestData": {}, | ||||
|     "subDomain": "iglootools.snowball.xyz" | ||||
|   }, | ||||
|   { | ||||
| @ -55,8 +55,8 @@ | ||||
|     "framework": "test-4", | ||||
|     "webhooks": [], | ||||
|     "icon": "", | ||||
|     "registryRecordId": "qbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", | ||||
|     "registryRecordData": {}, | ||||
|     "applicationDeploymentRequestId": "qbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", | ||||
|     "applicationDeploymentRequestData": {}, | ||||
|     "subDomain": "iglootools-2.snowball.xyz" | ||||
|   }, | ||||
|   { | ||||
| @ -70,8 +70,8 @@ | ||||
|     "framework": "test-5", | ||||
|     "webhooks": [], | ||||
|     "icon": "", | ||||
|     "registryRecordId": "xbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", | ||||
|     "registryRecordData": {}, | ||||
|     "applicationDeploymentRequestId": "xbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", | ||||
|     "applicationDeploymentRequestData": {}, | ||||
|     "subDomain": "snowball-2.snowball.xyz" | ||||
|   } | ||||
| ] | ||||
|  | ||||
| @ -41,8 +41,8 @@ async function main () { | ||||
|     const applicationDeploymentRecord = { | ||||
|       type: 'ApplicationDeploymentRecord', | ||||
|       version: '0.0.1', | ||||
|       name: deployment.registryRecordData.name, | ||||
|       application: deployment.registryRecordId, | ||||
|       name: deployment.applicationRecordData.name, | ||||
|       application: deployment.applicationRecordId, | ||||
| 
 | ||||
|       // TODO: Create DNS record
 | ||||
|       dns: 'bafyreihlymqggsgqiqawvehkpr2imt4l3u6q7um7xzjrux5rhsvwnuyewm', | ||||
| @ -53,7 +53,7 @@ async function main () { | ||||
|         so: '66fcfa49a1664d4cb4ce4f72c1c0e151' | ||||
|       }), | ||||
| 
 | ||||
|       request: deployment.project.registryRecordId, | ||||
|       request: deployment.project.applicationDeploymentRequestId, | ||||
|       url | ||||
|     }; | ||||
| 
 | ||||
|  | ||||
| @ -90,7 +90,9 @@ const DeploymentDetailsCard = ({ | ||||
|     <div className="grid grid-cols-4 gap-2 border-b border-gray-300 p-3 my-2"> | ||||
|       <div className="col-span-2"> | ||||
|         <div className="flex"> | ||||
|           <Typography className=" basis-3/4">{deployment.url}</Typography> | ||||
|           {deployment.url && ( | ||||
|             <Typography className=" basis-3/4">{deployment.url}</Typography> | ||||
|           )} | ||||
|           <Chip | ||||
|             value={deployment.status} | ||||
|             color={STATUS_COLORS[deployment.status] ?? 'gray'} | ||||
| @ -125,7 +127,7 @@ const DeploymentDetailsCard = ({ | ||||
|               target="_blank" | ||||
|               rel="noreferrer" | ||||
|             > | ||||
|               <MenuItem>^ Visit</MenuItem> | ||||
|               <MenuItem disabled={!Boolean(deployment.url)}>^ Visit</MenuItem> | ||||
|             </a> | ||||
|             <MenuItem | ||||
|               onClick={() => setAssignDomainDialog(!assignDomainDialog)} | ||||
|  | ||||
| @ -28,9 +28,11 @@ const DeploymentDialogBodyCard = ({ | ||||
|           color={chip.color} | ||||
|         /> | ||||
|       )} | ||||
|       <Typography variant="small" className="text-black"> | ||||
|         {deployment.url} | ||||
|       </Typography> | ||||
|       {deployment.url && ( | ||||
|         <Typography variant="small" className="text-black"> | ||||
|           {deployment.url} | ||||
|         </Typography> | ||||
|       )} | ||||
|       <Typography variant="small"> | ||||
|         ^ {deployment.branch} ^{' '} | ||||
|         {deployment.commitHash.substring(0, SHORT_COMMIT_HASH_LENGTH)}{' '} | ||||
|  | ||||
| @ -63,7 +63,7 @@ export type Deployment = { | ||||
|   branch: string | ||||
|   commitHash: string | ||||
|   commitMessage: string | ||||
|   url: string | ||||
|   url?: string | ||||
|   environment: Environment | ||||
|   isCurrent: boolean | ||||
|   status: DeploymentStatus | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user