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:
Nabarun Gogoi 2024-02-19 13:43:29 +05:30 committed by GitHub
parent b007286f4a
commit ce55fe62d8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 252 additions and 103 deletions

View File

@ -31,7 +31,7 @@
- Load fixtures in database - Load fixtures in database
```bash ```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) - 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 - Run the script to create bond, reserve the authority and set authority bond
```bash ```bash
yarn registry:init yarn test:registry:init
# snowball:initialize-registry bondId: 6af0ab81973b93d3511ae79841756fb5da3fd2f70ea1279e81fae7c9b19af6c4 +0ms # snowball:initialize-registry bondId: 6af0ab81973b93d3511ae79841756fb5da3fd2f70ea1279e81fae7c9b19af6c4 +0ms
``` ```

View File

@ -13,6 +13,7 @@
clientSecret = "" clientSecret = ""
[registryConfig] [registryConfig]
fetchDeploymentRecordDelay = 5000
restEndpoint = "http://localhost:1317" restEndpoint = "http://localhost:1317"
gqlEndpoint = "http://localhost:9473/api" gqlEndpoint = "http://localhost:9473/api"
chainId = "laconic_9000-1" chainId = "laconic_9000-1"

View File

@ -36,10 +36,10 @@
"lint": "eslint .", "lint": "eslint .",
"format": "prettier --write .", "format": "prettier --write .",
"format:check": "prettier --check .", "format:check": "prettier --check .",
"registry:init": "DEBUG=snowball:* ts-node ./test/initialize-registry.ts", "test:registry:init": "DEBUG=snowball:* ts-node ./test/initialize-registry.ts",
"registry:publish-deploy-records": "DEBUG=snowball:* ts-node ./test/publish-deploy-records.ts", "test:registry:publish-deploy-records": "DEBUG=snowball:* ts-node ./test/publish-deploy-records.ts",
"db:load:fixtures": "DEBUG=snowball:* ts-node ./test/initialize-db.ts", "test:db:load:fixtures": "DEBUG=snowball:* ts-node ./test/initialize-db.ts",
"db:delete": "DEBUG=snowball:* ts-node ./test/delete-db.ts" "test:db:delete": "DEBUG=snowball:* ts-node ./test/delete-db.ts"
}, },
"devDependencies": { "devDependencies": {
"@types/fs-extra": "^11.0.4", "@types/fs-extra": "^11.0.4",

View File

@ -22,6 +22,7 @@ export interface RegistryConfig {
chainId: string; chainId: string;
privateKey: string; privateKey: string;
bondId: string; bondId: string;
fetchDeploymentRecordDelay: number;
fee: { fee: {
amount: string; amount: string;
denom: string; denom: string;

View File

@ -126,8 +126,18 @@ export class Database {
return projects; 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[]> { async getDeploymentsByProjectId (projectId: string): Promise<Deployment[]> {
const deployments = await this.getDeployments({ return this.getDeployments({
relations: { relations: {
project: true, project: true,
domain: true, domain: true,
@ -142,15 +152,6 @@ export class Database {
createdAt: 'DESC' 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> { async getDeployment (options: FindOneOptions<Deployment>): Promise<Deployment | null> {
@ -167,7 +168,7 @@ export class Database {
return domains; return domains;
} }
async addDeployement (data: DeepPartial<Deployment>): Promise<Deployment> { async addDeployment (data: DeepPartial<Deployment>): Promise<Deployment> {
const deploymentRepository = this.dataSource.getRepository(Deployment); const deploymentRepository = this.dataSource.getRepository(Deployment);
const id = nanoid(); const id = nanoid();

View File

@ -12,6 +12,7 @@ import {
import { Project } from './Project'; import { Project } from './Project';
import { Domain } from './Domain'; import { Domain } from './Domain';
import { User } from './User'; import { User } from './User';
import { AppDeploymentRecordAttributes } from '../types';
export enum Environment { export enum Environment {
Production = 'Production', Production = 'Production',
@ -45,6 +46,9 @@ export class Deployment {
@PrimaryColumn('varchar') @PrimaryColumn('varchar')
id!: string; id!: string;
@Column()
projectId!: string;
@ManyToOne(() => Project, { onDelete: 'CASCADE' }) @ManyToOne(() => Project, { onDelete: 'CASCADE' })
@JoinColumn({ name: 'projectId' }) @JoinColumn({ name: 'projectId' })
project!: Project; project!: Project;
@ -69,10 +73,16 @@ export class Deployment {
url!: string | null; url!: string | null;
@Column('varchar') @Column('varchar')
registryRecordId!: string; applicationRecordId!: string;
@Column('simple-json') @Column('simple-json')
registryRecordData!: ApplicationRecord; applicationRecordData!: ApplicationRecord;
@Column('varchar', { nullable: true })
applicationDeploymentRecordId!: string | null;
@Column('simple-json', { nullable: true })
applicationDeploymentRecordData!: AppDeploymentRecordAttributes | null;
@Column({ @Column({
enum: Environment enum: Environment

View File

@ -53,10 +53,10 @@ export class Project {
prodBranch!: string; prodBranch!: string;
@Column('varchar', { nullable: true }) @Column('varchar', { nullable: true })
registryRecordId!: string | null; applicationDeploymentRequestId!: string | null;
@Column('simple-json', { nullable: true }) @Column('simple-json', { nullable: true })
registryRecordData!: ApplicationDeploymentRequest | null; applicationDeploymentRequestData!: ApplicationDeploymentRequest | null;
@Column('text', { default: '' }) @Column('text', { default: '' })
description!: string; description!: string;

View File

@ -31,7 +31,7 @@ export const main = async (): Promise<void> => {
await db.init(); await db.init();
const registry = new Registry(registryConfig); 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 typeDefs = fs.readFileSync(path.join(__dirname, 'schema.gql')).toString();
const resolvers = await createResolvers(service); const resolvers = await createResolvers(service);

View File

@ -7,13 +7,14 @@ import { Registry as LaconicRegistry } from '@cerc-io/laconic-sdk';
import { RegistryConfig } from './config'; import { RegistryConfig } from './config';
import { ApplicationDeploymentRequest } from './entity/Project'; import { ApplicationDeploymentRequest } from './entity/Project';
import { ApplicationRecord } from './entity/Deployment'; import { ApplicationRecord, Deployment } from './entity/Deployment';
import { PackageJSON } from './types'; import { AppDeploymentRecord, PackageJSON } from './types';
const log = debug('snowball:registry'); const log = debug('snowball:registry');
const APP_RECORD_TYPE = 'ApplicationRecord'; 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 // TODO: Move registry code to laconic-sdk/watcher-ts
export class Registry { export class Registry {
@ -35,7 +36,7 @@ export class Registry {
commitHash: string, commitHash: string,
appType: string, appType: string,
repoUrl: string repoUrl: string
}): Promise<{registryRecordId: string, registryRecordData: ApplicationRecord}> { }): Promise<{applicationRecordId: string, applicationRecordData: ApplicationRecord}> {
assert(packageJSON.name, "name field doesn't exist in package.json"); assert(packageJSON.name, "name field doesn't exist in package.json");
// Use laconic-sdk to publish record // 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 // 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.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); 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: { async createApplicationDeploymentRequest (data: {
@ -96,8 +97,8 @@ export class Registry {
repository: string, repository: string,
environmentVariables: { [key: string]: string } environmentVariables: { [key: string]: string }
}): Promise<{ }): Promise<{
registryRecordId: string, applicationDeploymentRequestId: string,
registryRecordData: ApplicationDeploymentRequest applicationDeploymentRequestData: ApplicationDeploymentRequest
}> { }> {
const crn = this.getCrn(data.appName); const crn = this.getCrn(data.appName);
const records = await this.registry.resolveNames([crn]); const records = await this.registry.resolveNames([crn]);
@ -109,7 +110,7 @@ export class Registry {
// Create record of type ApplicationDeploymentRequest and publish // Create record of type ApplicationDeploymentRequest and publish
const applicationDeploymentRequest = { const applicationDeploymentRequest = {
type: DEPLOYMENT_RECORD_TYPE, type: APP_DEPLOYMENT_REQUEST_TYPE,
version: '1.0.0', version: '1.0.0',
name: `${applicationRecord.attributes.name}@${applicationRecord.attributes.app_version}`, name: `${applicationRecord.attributes.name}@${applicationRecord.attributes.app_version}`,
application: `${crn}@${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 record published: ${result.data.id}`);
log('Application deployment request data:', applicationDeploymentRequest); 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 { getCrn (packageJsonName: string): string {

View File

@ -30,7 +30,7 @@ export const createResolvers = async (service: Service): Promise<any> => {
}, },
deployments: async (_: any, { projectId }: { projectId: string }) => { deployments: async (_: any, { projectId }: { projectId: string }) => {
return service.getDeployementsByProjectId(projectId); return service.getDeploymentsByProjectId(projectId);
}, },
environmentVariables: async (_: any, { projectId }: { projectId: string }) => { environmentVariables: async (_: any, { projectId }: { projectId: string }) => {

View File

@ -91,7 +91,7 @@ type Deployment {
branch: String! branch: String!
commitHash: String! commitHash: String!
commitMessage: String! commitMessage: String!
url: String! url: String
environment: Environment! environment: Environment!
isCurrent: Boolean! isCurrent: Boolean!
status: DeploymentStatus! status: DeploymentStatus!

View File

@ -14,14 +14,16 @@ import { Project } from './entity/Project';
import { Permission, ProjectMember } from './entity/ProjectMember'; import { Permission, ProjectMember } from './entity/ProjectMember';
import { User } from './entity/User'; import { User } from './entity/User';
import { Registry } from './registry'; import { Registry } from './registry';
import { GitHubConfig } from './config'; import { GitHubConfig, RegistryConfig } from './config';
import { GitPushEventPayload } from './types'; import { AppDeploymentRecord, GitPushEventPayload } from './types';
const log = debug('snowball:service'); const log = debug('snowball:service');
const GITHUB_UNIQUE_WEBHOOK_ERROR = 'Hook already exists on this repository'; const GITHUB_UNIQUE_WEBHOOK_ERROR = 'Hook already exists on this repository';
interface Config { interface Config {
gitHubConfig: GitHubConfig gitHubConfig: GitHubConfig
registryConfig: RegistryConfig
} }
export class Service { export class Service {
@ -30,11 +32,113 @@ export class Service {
private registry: Registry; private registry: Registry;
private config: Config; private config: Config;
private deployRecordCheckTimeout?: NodeJS.Timeout;
constructor (config: Config, db: Database, app: OAuthApp, registry: Registry) { constructor (config: Config, db: Database, app: OAuthApp, registry: Registry) {
this.db = db; this.db = db;
this.oauthApp = app; this.oauthApp = app;
this.registry = registry; this.registry = registry;
this.config = config; 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> { async getUser (userId: string): Promise<User | null> {
@ -67,7 +171,7 @@ export class Service {
return dbProjects; return dbProjects;
} }
async getDeployementsByProjectId (projectId: string): Promise<Deployment[]> { async getDeploymentsByProjectId (projectId: string): Promise<Deployment[]> {
const dbDeployments = await this.db.getDeploymentsByProjectId(projectId); const dbDeployments = await this.db.getDeploymentsByProjectId(projectId);
return dbDeployments; return dbDeployments;
} }
@ -187,7 +291,7 @@ export class Service {
const octokit = await this.getOctokit(userId); const octokit = await this.getOctokit(userId);
const newDeployement = await this.createDeployment(userId, const newDeployment = await this.createDeployment(userId,
octokit, octokit,
{ {
project: oldDeployment.project, project: oldDeployment.project,
@ -199,7 +303,7 @@ export class Service {
commitMessage: oldDeployment.commitMessage commitMessage: oldDeployment.commitMessage
}); });
return newDeployement; return newDeployment;
} }
async createDeployment ( 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) // 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, packageJSON,
appType: data.project!.template!, appType: data.project!.template!,
commitHash: data.commitHash!, commitHash: data.commitHash!,
repoUrl: recordData.repoUrl 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 // Update previous deployment with prod branch domain
// TODO: Fix unique constraint error for domain // TODO: Fix unique constraint error for domain
await this.db.updateDeployment({ await this.db.updateDeployment({
@ -258,7 +351,7 @@ export class Service {
domain: null domain: null
}); });
const newDeployement = await this.db.addDeployement({ const newDeployment = await this.db.addDeployment({
project: data.project, project: data.project,
branch: data.branch, branch: data.branch,
commitHash: data.commitHash, commitHash: data.commitHash,
@ -266,16 +359,16 @@ export class Service {
environment: data.environment, environment: data.environment,
isCurrent: data.isCurrent, isCurrent: data.isCurrent,
status: DeploymentStatus.Building, status: DeploymentStatus.Building,
registryRecordId, applicationRecordId,
registryRecordData, applicationRecordData,
domain: data.domain, domain: data.domain,
createdBy: Object.assign(new User(), { createdBy: Object.assign(new User(), {
id: userId id: userId
}) })
}); });
log(`Created deployment ${newDeployement.id} and published application record ${registryRecordId}`); log(`Created deployment ${newDeployment.id} and published application record ${applicationRecordId}`);
return newDeployement; return newDeployment;
} }
async addProject (userId: string, organizationSlug: string, data: DeepPartial<Project>): Promise<Project | undefined> { async addProject (userId: string, organizationSlug: string, data: DeepPartial<Project>): Promise<Project | undefined> {
@ -327,17 +420,17 @@ export class Service {
return acc; return acc;
}, {} as { [key: string]: string }); }, {} 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, commitHash: latestCommit.sha,
repository: repoDetails.html_url, repository: repoDetails.html_url,
environmentVariables: environmentVariablesObj environmentVariables: environmentVariablesObj
}); });
await this.db.updateProjectById(project.id, { await this.db.updateProjectById(project.id, {
registryRecordId, applicationDeploymentRequestId,
registryRecordData applicationDeploymentRequestData
}); });
await this.createRepoHook(octokit, project); await this.createRepoHook(octokit, project);
@ -444,7 +537,7 @@ export class Service {
const octokit = await this.getOctokit(userId); const octokit = await this.getOctokit(userId);
const newDeployement = await this.createDeployment(userId, const newDeployment = await this.createDeployment(userId,
octokit, octokit,
{ {
project: oldDeployment.project, project: oldDeployment.project,
@ -457,7 +550,7 @@ export class Service {
commitMessage: oldDeployment.commitMessage commitMessage: oldDeployment.commitMessage
}); });
return newDeployement; return newDeployment;
} }
async rollbackDeployment (projectId: string, deploymentId: string): Promise<boolean> { async rollbackDeployment (projectId: string, deploymentId: string): Promise<boolean> {
@ -475,7 +568,7 @@ export class Service {
}); });
if (!oldCurrentDeployment) { 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 }); const oldCurrentDeploymentUpdate = await this.db.updateDeploymentById(oldCurrentDeployment.id, { isCurrent: false, domain: null });

View File

@ -25,3 +25,27 @@ export interface GitPushEventPayload {
message: string; 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
}

View File

@ -7,8 +7,8 @@
"status": "Ready", "status": "Ready",
"environment": "Production", "environment": "Production",
"isCurrent": true, "isCurrent": true,
"registryRecordId": "qbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", "applicationRecordId": "qbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
"registryRecordData": {}, "applicationRecordData": {},
"branch": "main", "branch": "main",
"commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00",
"commitMessage": "subscription added", "commitMessage": "subscription added",
@ -22,8 +22,8 @@
"status": "Ready", "status": "Ready",
"environment": "Preview", "environment": "Preview",
"isCurrent": false, "isCurrent": false,
"registryRecordId": "wbafyreihvzya6ovp4yfpkqnddkui2iw7thbhwq74lbqs7bhobvmfhrowoi", "applicationRecordId": "wbafyreihvzya6ovp4yfpkqnddkui2iw7thbhwq74lbqs7bhobvmfhrowoi",
"registryRecordData": {}, "applicationRecordData": {},
"branch": "test", "branch": "test",
"commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00",
"commitMessage": "subscription added", "commitMessage": "subscription added",
@ -37,8 +37,8 @@
"status": "Ready", "status": "Ready",
"environment": "Development", "environment": "Development",
"isCurrent": false, "isCurrent": false,
"registryRecordId": "ebafyreihvzya6ovp4yfpkqnddkui2iw7t6bhwq74lbqs7bhobvmfhrowoi", "applicationRecordId": "ebafyreihvzya6ovp4yfpkqnddkui2iw7t6bhwq74lbqs7bhobvmfhrowoi",
"registryRecordData": {}, "applicationRecordData": {},
"branch": "test", "branch": "test",
"commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00",
"commitMessage": "subscription added", "commitMessage": "subscription added",
@ -52,8 +52,8 @@
"status": "Ready", "status": "Ready",
"environment": "Production", "environment": "Production",
"isCurrent": false, "isCurrent": false,
"registryRecordId": "rbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhw74lbqs7bhobvmfhrowoi", "applicationRecordId": "rbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhw74lbqs7bhobvmfhrowoi",
"registryRecordData": {}, "applicationRecordData": {},
"branch": "prod", "branch": "prod",
"commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00",
"commitMessage": "subscription added", "commitMessage": "subscription added",
@ -67,8 +67,8 @@
"status": "Ready", "status": "Ready",
"environment": "Production", "environment": "Production",
"isCurrent": true, "isCurrent": true,
"registryRecordId": "tbafyreihvzya6ovp4yfpqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", "applicationRecordId": "tbafyreihvzya6ovp4yfpqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
"registryRecordData": {}, "applicationRecordData": {},
"branch": "main", "branch": "main",
"commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00",
"commitMessage": "subscription added", "commitMessage": "subscription added",
@ -82,8 +82,8 @@
"status": "Ready", "status": "Ready",
"environment": "Preview", "environment": "Preview",
"isCurrent": false, "isCurrent": false,
"registryRecordId": "ybafyreihvzya6ovp4yfpkqnddkui2iw7t6bhwq74lbqs7bhobvmfhrowoi", "applicationRecordId": "ybafyreihvzya6ovp4yfpkqnddkui2iw7t6bhwq74lbqs7bhobvmfhrowoi",
"registryRecordData": {}, "applicationRecordData": {},
"branch": "test", "branch": "test",
"commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00",
"commitMessage": "subscription added", "commitMessage": "subscription added",
@ -97,8 +97,8 @@
"status": "Ready", "status": "Ready",
"environment": "Development", "environment": "Development",
"isCurrent": false, "isCurrent": false,
"registryRecordId": "ubafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvfhrowoi", "applicationRecordId": "ubafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvfhrowoi",
"registryRecordData": {}, "applicationRecordData": {},
"branch": "test", "branch": "test",
"commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00",
"commitMessage": "subscription added", "commitMessage": "subscription added",
@ -112,8 +112,8 @@
"status": "Ready", "status": "Ready",
"environment": "Production", "environment": "Production",
"isCurrent": true, "isCurrent": true,
"registryRecordId": "ibayreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", "applicationRecordId": "ibayreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
"registryRecordData": {}, "applicationRecordData": {},
"branch": "main", "branch": "main",
"commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00",
"commitMessage": "subscription added", "commitMessage": "subscription added",
@ -127,8 +127,8 @@
"status": "Ready", "status": "Ready",
"environment": "Preview", "environment": "Preview",
"isCurrent": false, "isCurrent": false,
"registryRecordId": "obafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", "applicationRecordId": "obafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
"registryRecordData": {}, "applicationRecordData": {},
"branch": "test", "branch": "test",
"commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00",
"commitMessage": "subscription added", "commitMessage": "subscription added",
@ -142,8 +142,8 @@
"status": "Ready", "status": "Ready",
"environment": "Development", "environment": "Development",
"isCurrent": false, "isCurrent": false,
"registryRecordId": "pbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowo", "applicationRecordId": "pbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowo",
"registryRecordData": {}, "applicationRecordData": {},
"branch": "test", "branch": "test",
"commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00",
"commitMessage": "subscription added", "commitMessage": "subscription added",
@ -157,8 +157,8 @@
"status": "Ready", "status": "Ready",
"environment": "Production", "environment": "Production",
"isCurrent": true, "isCurrent": true,
"registryRecordId": "pbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowo", "applicationRecordId": "pbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowo",
"registryRecordData": {}, "applicationRecordData": {},
"branch": "test", "branch": "test",
"commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00",
"commitMessage": "subscription added", "commitMessage": "subscription added",

View File

@ -10,8 +10,8 @@
"framework": "test", "framework": "test",
"webhooks": [], "webhooks": [],
"icon": "", "icon": "",
"registryRecordId": "hbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", "applicationDeploymentRequestId": "hbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
"registryRecordData": {}, "applicationDeploymentRequestData": {},
"subDomain": "testProject.snowball.xyz" "subDomain": "testProject.snowball.xyz"
}, },
{ {
@ -25,8 +25,8 @@
"framework": "test-2", "framework": "test-2",
"webhooks": [], "webhooks": [],
"icon": "", "icon": "",
"registryRecordId": "gbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", "applicationDeploymentRequestId": "gbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
"registryRecordData": {}, "applicationDeploymentRequestData": {},
"subDomain": "testProject-2.snowball.xyz" "subDomain": "testProject-2.snowball.xyz"
}, },
{ {
@ -40,8 +40,8 @@
"framework": "test-3", "framework": "test-3",
"webhooks": [], "webhooks": [],
"icon": "", "icon": "",
"registryRecordId": "ebafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", "applicationDeploymentRequestId": "ebafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
"registryRecordData": {}, "applicationDeploymentRequestData": {},
"subDomain": "iglootools.snowball.xyz" "subDomain": "iglootools.snowball.xyz"
}, },
{ {
@ -55,8 +55,8 @@
"framework": "test-4", "framework": "test-4",
"webhooks": [], "webhooks": [],
"icon": "", "icon": "",
"registryRecordId": "qbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", "applicationDeploymentRequestId": "qbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
"registryRecordData": {}, "applicationDeploymentRequestData": {},
"subDomain": "iglootools-2.snowball.xyz" "subDomain": "iglootools-2.snowball.xyz"
}, },
{ {
@ -70,8 +70,8 @@
"framework": "test-5", "framework": "test-5",
"webhooks": [], "webhooks": [],
"icon": "", "icon": "",
"registryRecordId": "xbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", "applicationDeploymentRequestId": "xbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
"registryRecordData": {}, "applicationDeploymentRequestData": {},
"subDomain": "snowball-2.snowball.xyz" "subDomain": "snowball-2.snowball.xyz"
} }
] ]

View File

@ -41,8 +41,8 @@ async function main () {
const applicationDeploymentRecord = { const applicationDeploymentRecord = {
type: 'ApplicationDeploymentRecord', type: 'ApplicationDeploymentRecord',
version: '0.0.1', version: '0.0.1',
name: deployment.registryRecordData.name, name: deployment.applicationRecordData.name,
application: deployment.registryRecordId, application: deployment.applicationRecordId,
// TODO: Create DNS record // TODO: Create DNS record
dns: 'bafyreihlymqggsgqiqawvehkpr2imt4l3u6q7um7xzjrux5rhsvwnuyewm', dns: 'bafyreihlymqggsgqiqawvehkpr2imt4l3u6q7um7xzjrux5rhsvwnuyewm',
@ -53,7 +53,7 @@ async function main () {
so: '66fcfa49a1664d4cb4ce4f72c1c0e151' so: '66fcfa49a1664d4cb4ce4f72c1c0e151'
}), }),
request: deployment.project.registryRecordId, request: deployment.project.applicationDeploymentRequestId,
url url
}; };

View File

@ -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="grid grid-cols-4 gap-2 border-b border-gray-300 p-3 my-2">
<div className="col-span-2"> <div className="col-span-2">
<div className="flex"> <div className="flex">
{deployment.url && (
<Typography className=" basis-3/4">{deployment.url}</Typography> <Typography className=" basis-3/4">{deployment.url}</Typography>
)}
<Chip <Chip
value={deployment.status} value={deployment.status}
color={STATUS_COLORS[deployment.status] ?? 'gray'} color={STATUS_COLORS[deployment.status] ?? 'gray'}
@ -125,7 +127,7 @@ const DeploymentDetailsCard = ({
target="_blank" target="_blank"
rel="noreferrer" rel="noreferrer"
> >
<MenuItem>^ Visit</MenuItem> <MenuItem disabled={!Boolean(deployment.url)}>^ Visit</MenuItem>
</a> </a>
<MenuItem <MenuItem
onClick={() => setAssignDomainDialog(!assignDomainDialog)} onClick={() => setAssignDomainDialog(!assignDomainDialog)}

View File

@ -28,9 +28,11 @@ const DeploymentDialogBodyCard = ({
color={chip.color} color={chip.color}
/> />
)} )}
{deployment.url && (
<Typography variant="small" className="text-black"> <Typography variant="small" className="text-black">
{deployment.url} {deployment.url}
</Typography> </Typography>
)}
<Typography variant="small"> <Typography variant="small">
^ {deployment.branch} ^{' '} ^ {deployment.branch} ^{' '}
{deployment.commitHash.substring(0, SHORT_COMMIT_HASH_LENGTH)}{' '} {deployment.commitHash.substring(0, SHORT_COMMIT_HASH_LENGTH)}{' '}

View File

@ -63,7 +63,7 @@ export type Deployment = {
branch: string branch: string
commitHash: string commitHash: string
commitMessage: string commitMessage: string
url: string url?: string
environment: Environment environment: Environment
isCurrent: boolean isCurrent: boolean
status: DeploymentStatus status: DeploymentStatus