mirror of
https://github.com/snowball-tools/snowballtools-base.git
synced 2024-12-22 16:37:44 +00:00
Set record data with repo commit hash and package.json content (#65)
* Get latest commit hash from repo when adding project * Update UI/UX * Fill registry record with data from package.json * Add package json type * Correct record data based on laconic console * Update README --------- Co-authored-by: neeraj <neeraj.rtly@gmail.com>
This commit is contained in:
parent
76dfd3bb76
commit
9144d42f70
22
README.md
22
README.md
@ -77,21 +77,31 @@
|
||||
# 0.0.0.0:32771
|
||||
```
|
||||
|
||||
- Reserve authority for `snowball`
|
||||
- Reserve authorities for `snowballtools` and `cerc-io`
|
||||
|
||||
```bash
|
||||
laconic-so --stack fixturenet-laconic-loaded deploy exec cli "laconic cns authority reserve snowball"
|
||||
laconic-so --stack fixturenet-laconic-loaded deploy exec cli "laconic cns authority reserve snowballtools"
|
||||
# {"success":true}
|
||||
```
|
||||
|
||||
- Set authority bond
|
||||
|
||||
```bash
|
||||
laconic-so --stack fixturenet-laconic-loaded deploy exec cli "laconic cns authority bond set snowball $BOND_ID"
|
||||
laconic-so --stack fixturenet-laconic-loaded deploy exec cli "laconic cns authority reserve cerc-io"
|
||||
# {"success":true}
|
||||
```
|
||||
|
||||
- Start the server
|
||||
- Set authority bond for `snowballtools` and `cerc-io`
|
||||
|
||||
```bash
|
||||
laconic-so --stack fixturenet-laconic-loaded deploy exec cli "laconic cns authority bond set snowballtools $BOND_ID"
|
||||
# {"success":true}
|
||||
```
|
||||
|
||||
```bash
|
||||
laconic-so --stack fixturenet-laconic-loaded deploy exec cli "laconic cns authority bond set cerc-io $BOND_ID"
|
||||
# {"success":true}
|
||||
```
|
||||
|
||||
- Start the server in `packages/backend`
|
||||
|
||||
```bash
|
||||
yarn start
|
||||
|
@ -16,8 +16,10 @@
|
||||
"express": "^4.18.2",
|
||||
"fs-extra": "^11.2.0",
|
||||
"graphql": "^16.8.1",
|
||||
"luxon": "^3.4.4",
|
||||
"nanoid": "3",
|
||||
"nanoid-dictionary": "^5.0.0-beta.1",
|
||||
"octokit": "^3.1.2",
|
||||
"reflect-metadata": "^0.2.1",
|
||||
"semver": "^7.6.0",
|
||||
"toml": "^3.0.0",
|
||||
|
@ -28,13 +28,13 @@ export enum DeploymentStatus {
|
||||
export interface ApplicationRecord {
|
||||
type: string;
|
||||
version:string
|
||||
name: string
|
||||
description: string
|
||||
homepage: string
|
||||
license: string
|
||||
author: string
|
||||
repository: string,
|
||||
app_version: string
|
||||
name?: string
|
||||
description?: string
|
||||
homepage?: string
|
||||
license?: string
|
||||
author?: string
|
||||
repository?: string[],
|
||||
app_version?: string
|
||||
repository_ref: string
|
||||
app_type: string
|
||||
}
|
||||
|
@ -20,14 +20,8 @@ export interface ApplicationDeploymentRequest {
|
||||
version: string
|
||||
name: string
|
||||
application: string
|
||||
config: {
|
||||
env: {[key:string]: string}
|
||||
},
|
||||
meta: {
|
||||
note: string
|
||||
repository: string
|
||||
repository_ref: string
|
||||
}
|
||||
config: string,
|
||||
meta: string
|
||||
}
|
||||
|
||||
@Entity()
|
||||
|
@ -1,17 +1,19 @@
|
||||
import debug from 'debug';
|
||||
import assert from 'assert';
|
||||
import { inc as semverInc } from 'semver';
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
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';
|
||||
|
||||
const log = debug('snowball:registry');
|
||||
|
||||
const APP_RECORD_TYPE = 'ApplicationRecord';
|
||||
const DEPLOYMENT_RECORD_TYPE = 'ApplicationDeploymentRequest';
|
||||
const AUTHORITY_NAME = 'snowball';
|
||||
|
||||
// TODO: Move registry code to laconic-sdk/watcher-ts
|
||||
export class Registry {
|
||||
@ -23,16 +25,21 @@ export class Registry {
|
||||
this.registry = new LaconicRegistry(registryConfig.gqlEndpoint, registryConfig.restEndpoint, registryConfig.chainId);
|
||||
}
|
||||
|
||||
async createApplicationRecord (data: { recordName: string, appType: string }): Promise<{registryRecordId: string, registryRecordData: ApplicationRecord}> {
|
||||
// TODO: Get record name from repo package.json name
|
||||
const recordName = data.recordName;
|
||||
|
||||
async createApplicationRecord ({
|
||||
packageJSON,
|
||||
commitHash,
|
||||
appType
|
||||
}: {
|
||||
packageJSON: PackageJSON
|
||||
appType: string,
|
||||
commitHash: string
|
||||
}): Promise<{registryRecordId: string, registryRecordData: ApplicationRecord}> {
|
||||
// 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
|
||||
const records = await this.registry.queryRecords({
|
||||
type: APP_RECORD_TYPE,
|
||||
name: recordName
|
||||
name: packageJSON.name
|
||||
}, true);
|
||||
|
||||
// Get next version of record
|
||||
@ -40,23 +47,21 @@ export class Registry {
|
||||
const [latestBondRecord] = bondRecords.sort((a: any, b: any) => new Date(b.createTime).getTime() - new Date(a.createTime).getTime());
|
||||
const nextVersion = semverInc(latestBondRecord?.attributes.version ?? '0.0.0', 'patch');
|
||||
|
||||
assert(nextVersion, 'Application record version not valid');
|
||||
|
||||
// Create record of type ApplicationRecord and publish
|
||||
const applicationRecord = {
|
||||
type: APP_RECORD_TYPE,
|
||||
version: nextVersion ?? '',
|
||||
name: recordName,
|
||||
|
||||
// TODO: Get data from repo package.json
|
||||
description: '',
|
||||
homepage: '',
|
||||
license: '',
|
||||
author: '',
|
||||
repository: '',
|
||||
app_version: '0.1.0',
|
||||
|
||||
// TODO: Get latest commit hash from repo production branch / deployment
|
||||
repository_ref: '10ac6678e8372a05ad5bb1c34c34',
|
||||
app_type: data.appType
|
||||
version: nextVersion,
|
||||
repository_ref: commitHash,
|
||||
app_type: appType,
|
||||
...(packageJSON.name && { name: packageJSON.name }),
|
||||
...(packageJSON.description && { description: packageJSON.description }),
|
||||
...(packageJSON.homepage && { homepage: packageJSON.homepage }),
|
||||
...(packageJSON.license && { license: packageJSON.license }),
|
||||
...(packageJSON.author && { author: typeof packageJSON.author === 'object' ? JSON.stringify(packageJSON.author) : packageJSON.author }),
|
||||
...(packageJSON.repository && { repository: [packageJSON.repository] }),
|
||||
...(packageJSON.version && { app_version: packageJSON.version })
|
||||
};
|
||||
|
||||
const result = await this.registry.setRecord(
|
||||
@ -71,8 +76,8 @@ export class Registry {
|
||||
|
||||
log('Application record data:', applicationRecord);
|
||||
|
||||
// TODO: Discuss computation of crn
|
||||
const crn = this.getCrn(data.recordName);
|
||||
// TODO: Discuss computation of CRN
|
||||
const crn = this.getCrn(packageJSON.name ?? '');
|
||||
|
||||
await this.registry.setName({ cid: result.data.id, crn }, 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);
|
||||
@ -81,7 +86,14 @@ export class Registry {
|
||||
return { registryRecordId: result.data.id, registryRecordData: applicationRecord };
|
||||
}
|
||||
|
||||
async createApplicationDeploymentRequest (data: { appName: string }): Promise<{registryRecordId: string, registryRecordData: ApplicationDeploymentRequest}> {
|
||||
async createApplicationDeploymentRequest (data: {
|
||||
appName: string,
|
||||
commitHash: string,
|
||||
repository: string
|
||||
}): Promise<{
|
||||
registryRecordId: string,
|
||||
registryRecordData: ApplicationDeploymentRequest
|
||||
}> {
|
||||
const crn = this.getCrn(data.appName);
|
||||
const records = await this.registry.resolveNames([crn]);
|
||||
const applicationRecord = records[0];
|
||||
@ -101,16 +113,17 @@ export class Registry {
|
||||
// dns: '$CERC_REGISTRY_DEPLOYMENT_SHORT_HOSTNAME',
|
||||
// deployment: '$CERC_REGISTRY_DEPLOYMENT_CRN',
|
||||
|
||||
config: {
|
||||
// https://git.vdb.to/cerc-io/laconic-registry-cli/commit/129019105dfb93bebcea02fde0ed64d0f8e5983b
|
||||
config: JSON.stringify({
|
||||
env: {
|
||||
CERC_WEBAPP_DEBUG: `${applicationRecord.attributes.app_version}`
|
||||
}
|
||||
},
|
||||
meta: {
|
||||
note: `Added by Snowball @ ${(new Date()).toISOString()}`,
|
||||
repository: applicationRecord.attributes.repository,
|
||||
repository_ref: applicationRecord.attributes.repository_ref
|
||||
}
|
||||
}),
|
||||
meta: JSON.stringify({
|
||||
note: `Added by Snowball @ ${DateTime.utc().toFormat('EEE LLL dd HH:mm:ss \'UTC\' yyyy')}`,
|
||||
repository: data.repository,
|
||||
repository_ref: data.commitHash
|
||||
})
|
||||
};
|
||||
|
||||
const result = await this.registry.setRecord(
|
||||
@ -128,7 +141,14 @@ export class Registry {
|
||||
return { registryRecordId: result.data.id, registryRecordData: applicationDeploymentRequest };
|
||||
}
|
||||
|
||||
getCrn (appName: string): string {
|
||||
return `crn://${AUTHORITY_NAME}/applications/${appName}`;
|
||||
getCrn (packageJsonName: string): string {
|
||||
const [arg1, arg2] = packageJsonName.split('/');
|
||||
|
||||
if (arg2) {
|
||||
const authority = arg1.replace('@', '');
|
||||
return `crn://${authority}/applications/${arg2}`;
|
||||
}
|
||||
|
||||
return `crn://${arg1}/applications/${arg1}`;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import assert from 'assert';
|
||||
import debug from 'debug';
|
||||
import { DeepPartial, FindOptionsWhere } from 'typeorm';
|
||||
import { Octokit } from 'octokit';
|
||||
|
||||
import { OAuthApp } from '@octokit/oauth-app';
|
||||
|
||||
@ -18,12 +19,12 @@ const log = debug('snowball:service');
|
||||
|
||||
export class Service {
|
||||
private db: Database;
|
||||
private app: OAuthApp;
|
||||
private oauthApp: OAuthApp;
|
||||
private registry: Registry;
|
||||
|
||||
constructor (db: Database, app: OAuthApp, registry: Registry) {
|
||||
this.db = db;
|
||||
this.app = app;
|
||||
this.oauthApp = app;
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
@ -35,6 +36,13 @@ export class Service {
|
||||
});
|
||||
}
|
||||
|
||||
async getOctokit (userId: string): Promise<Octokit> {
|
||||
const user = await this.db.getUser({ where: { id: userId } });
|
||||
assert(user && user.gitHubToken, 'User needs to be authenticated with GitHub token');
|
||||
|
||||
return new Octokit({ auth: user.gitHubToken });
|
||||
}
|
||||
|
||||
async getOrganizationsByUserId (userId: string): Promise<Organization[]> {
|
||||
const dbOrganizations = await this.db.getOrganizationsByUserId(userId);
|
||||
return dbOrganizations;
|
||||
@ -200,7 +208,10 @@ export class Service {
|
||||
await this.db.updateDeploymentById(oldCurrentDeployment.id, { isCurrent: false, domain: null });
|
||||
}
|
||||
|
||||
const octokit = await this.getOctokit(userId);
|
||||
|
||||
const newDeployement = await this.createDeployment(userId,
|
||||
octokit,
|
||||
{
|
||||
project: oldDeployment.project,
|
||||
isCurrent: true,
|
||||
@ -213,10 +224,28 @@ export class Service {
|
||||
return newDeployement;
|
||||
}
|
||||
|
||||
async createDeployment (userId: string, data: DeepPartial<Deployment>): Promise<Deployment> {
|
||||
async createDeployment (userId: string, octokit: Octokit, data: DeepPartial<Deployment>): Promise<Deployment> {
|
||||
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
|
||||
});
|
||||
|
||||
if (!packageJSONData) {
|
||||
throw new Error('Package.json file not found');
|
||||
}
|
||||
|
||||
assert(!Array.isArray(packageJSONData) && packageJSONData.type === 'file');
|
||||
const packageJSON = JSON.parse(atob(packageJSONData.content));
|
||||
|
||||
const { registryRecordId, registryRecordData } = await this.registry.createApplicationRecord({
|
||||
recordName: data.project?.name ?? '',
|
||||
appType: data.project?.template ?? ''
|
||||
packageJSON,
|
||||
appType: data.project!.template!,
|
||||
commitHash: data.commitHash!
|
||||
});
|
||||
|
||||
const newDeployement = await this.db.addDeployement({
|
||||
@ -250,24 +279,43 @@ export class Service {
|
||||
|
||||
const project = await this.db.addProject(userId, organization.id, data);
|
||||
|
||||
// TODO: Get repository details from github
|
||||
await this.createDeployment(userId,
|
||||
const octokit = await this.getOctokit(userId);
|
||||
const [owner, repo] = project.repository.split('/');
|
||||
|
||||
const { data: [latestCommit] } = await octokit.rest.repos.listCommits({
|
||||
owner,
|
||||
repo,
|
||||
sha: project.prodBranch,
|
||||
per_page: 1
|
||||
});
|
||||
|
||||
const { data: repoDetails } = await octokit.rest.repos.get({ owner, repo });
|
||||
|
||||
// Create deployment with prod branch and latest commit
|
||||
const newDeployment = await this.createDeployment(userId,
|
||||
octokit,
|
||||
{
|
||||
project,
|
||||
isCurrent: true,
|
||||
branch: project.prodBranch,
|
||||
environment: Environment.Production,
|
||||
// TODO: Set latest commit hash
|
||||
commitHash: '',
|
||||
domain: null
|
||||
domain: null,
|
||||
commitHash: latestCommit.sha
|
||||
});
|
||||
|
||||
const { registryRecordId, registryRecordData } = await this.registry.createApplicationDeploymentRequest({ appName: project.name });
|
||||
const { registryRecordId, registryRecordData } = await this.registry.createApplicationDeploymentRequest(
|
||||
{
|
||||
appName: newDeployment.registryRecordData.name!,
|
||||
commitHash: latestCommit.sha,
|
||||
repository: repoDetails.git_url
|
||||
});
|
||||
await this.db.updateProjectById(project.id, {
|
||||
registryRecordId,
|
||||
registryRecordData
|
||||
});
|
||||
|
||||
// TODO: Setup repo webhook for push events
|
||||
|
||||
return project;
|
||||
}
|
||||
|
||||
@ -311,7 +359,10 @@ export class Service {
|
||||
|
||||
await this.db.updateDeploymentById(deploymentId, { domain: null, isCurrent: false });
|
||||
|
||||
const octokit = await this.getOctokit(userId);
|
||||
|
||||
const newDeployement = await this.createDeployment(userId,
|
||||
octokit,
|
||||
{
|
||||
project: oldDeployment.project,
|
||||
// TODO: Put isCurrent field in project
|
||||
@ -435,7 +486,7 @@ export class Service {
|
||||
}
|
||||
|
||||
async authenticateGitHub (code:string, userId: string): Promise<{token: string}> {
|
||||
const { authentication: { token } } = await this.app.createToken({
|
||||
const { authentication: { token } } = await this.oauthApp.createToken({
|
||||
code
|
||||
});
|
||||
|
||||
|
9
packages/backend/src/types.ts
Normal file
9
packages/backend/src/types.ts
Normal file
@ -0,0 +1,9 @@
|
||||
export interface PackageJSON {
|
||||
name?: string;
|
||||
version?: string;
|
||||
author?: string;
|
||||
description?: string;
|
||||
homepage?: string;
|
||||
license?: string;
|
||||
repository?: string;
|
||||
}
|
20
packages/backend/test/fixtures/deployments.json
vendored
20
packages/backend/test/fixtures/deployments.json
vendored
@ -10,7 +10,7 @@
|
||||
"registryRecordId": "qbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
|
||||
"registryRecordData": {},
|
||||
"branch": "main",
|
||||
"commitHash": "testXyz",
|
||||
"commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00",
|
||||
"url": "testProject-ffhae3zq.snowball.xyz"
|
||||
},
|
||||
{
|
||||
@ -24,7 +24,7 @@
|
||||
"registryRecordId": "wbafyreihvzya6ovp4yfpkqnddkui2iw7thbhwq74lbqs7bhobvmfhrowoi",
|
||||
"registryRecordData": {},
|
||||
"branch": "test",
|
||||
"commitHash": "testXyz",
|
||||
"commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00",
|
||||
"url": "testProject-vehagei8.snowball.xyz"
|
||||
},
|
||||
{
|
||||
@ -38,7 +38,7 @@
|
||||
"registryRecordId": "ebafyreihvzya6ovp4yfpkqnddkui2iw7t6bhwq74lbqs7bhobvmfhrowoi",
|
||||
"registryRecordData": {},
|
||||
"branch": "test",
|
||||
"commitHash": "testXyz",
|
||||
"commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00",
|
||||
"url": "testProject-qmgekyte.snowball.xyz"
|
||||
},
|
||||
{
|
||||
@ -52,7 +52,7 @@
|
||||
"registryRecordId": "rbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhw74lbqs7bhobvmfhrowoi",
|
||||
"registryRecordData": {},
|
||||
"branch": "prod",
|
||||
"commitHash": "testXyz",
|
||||
"commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00",
|
||||
"url": "testProject-f8wsyim6.snowball.xyz"
|
||||
},
|
||||
{
|
||||
@ -66,7 +66,7 @@
|
||||
"registryRecordId": "tbafyreihvzya6ovp4yfpqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
|
||||
"registryRecordData": {},
|
||||
"branch": "main",
|
||||
"commitHash": "testXyz",
|
||||
"commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00",
|
||||
"url": "testProject-2-eO8cckxk.snowball.xyz"
|
||||
},
|
||||
{
|
||||
@ -80,7 +80,7 @@
|
||||
"registryRecordId": "ybafyreihvzya6ovp4yfpkqnddkui2iw7t6bhwq74lbqs7bhobvmfhrowoi",
|
||||
"registryRecordData": {},
|
||||
"branch": "test",
|
||||
"commitHash": "testXyz",
|
||||
"commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00",
|
||||
"url": "testProject-2-yaq0t5yw.snowball.xyz"
|
||||
},
|
||||
{
|
||||
@ -94,7 +94,7 @@
|
||||
"registryRecordId": "ubafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvfhrowoi",
|
||||
"registryRecordData": {},
|
||||
"branch": "test",
|
||||
"commitHash": "testXyz",
|
||||
"commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00",
|
||||
"url": "testProject-2-hwwr6sbx.snowball.xyz"
|
||||
},
|
||||
{
|
||||
@ -108,7 +108,7 @@
|
||||
"registryRecordId": "ibayreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
|
||||
"registryRecordData": {},
|
||||
"branch": "main",
|
||||
"commitHash": "testXyz",
|
||||
"commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00",
|
||||
"url": "iglootools-ndxje48a.snowball.xyz"
|
||||
},
|
||||
{
|
||||
@ -122,7 +122,7 @@
|
||||
"registryRecordId": "obafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
|
||||
"registryRecordData": {},
|
||||
"branch": "test",
|
||||
"commitHash": "testXyz",
|
||||
"commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00",
|
||||
"url": "iglootools-gtgpgvei.snowball.xyz"
|
||||
},
|
||||
{
|
||||
@ -136,7 +136,7 @@
|
||||
"registryRecordId": "pbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowo",
|
||||
"registryRecordData": {},
|
||||
"branch": "test",
|
||||
"commitHash": "testXyz",
|
||||
"commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00",
|
||||
"url": "iglootools-b4bpthjr.snowball.xyz"
|
||||
}
|
||||
]
|
||||
|
20
packages/backend/test/fixtures/projects.json
vendored
20
packages/backend/test/fixtures/projects.json
vendored
@ -3,10 +3,10 @@
|
||||
"ownerIndex": 0,
|
||||
"organizationIndex": 0,
|
||||
"name": "testProject",
|
||||
"repository": "test",
|
||||
"repository": "snowball-tools/snowball-ts-framework-template",
|
||||
"prodBranch": "main",
|
||||
"description": "test",
|
||||
"template": "test",
|
||||
"template": "webapp",
|
||||
"framework": "test",
|
||||
"webhooks": [],
|
||||
"icon": "",
|
||||
@ -18,10 +18,10 @@
|
||||
"ownerIndex": 1,
|
||||
"organizationIndex": 0,
|
||||
"name": "testProject-2",
|
||||
"repository": "test-2",
|
||||
"repository": "snowball-tools/snowball-ts-framework-template",
|
||||
"prodBranch": "main",
|
||||
"description": "test-2",
|
||||
"template": "test-2",
|
||||
"template": "webapp",
|
||||
"framework": "test-2",
|
||||
"webhooks": [],
|
||||
"icon": "",
|
||||
@ -33,10 +33,10 @@
|
||||
"ownerIndex": 2,
|
||||
"organizationIndex": 0,
|
||||
"name": "iglootools",
|
||||
"repository": "test-3",
|
||||
"repository": "snowball-tools/snowball-ts-framework-template",
|
||||
"prodBranch": "main",
|
||||
"description": "test-3",
|
||||
"template": "test-3",
|
||||
"template": "webapp",
|
||||
"framework": "test-3",
|
||||
"webhooks": [],
|
||||
"icon": "",
|
||||
@ -48,10 +48,10 @@
|
||||
"ownerIndex": 1,
|
||||
"organizationIndex": 0,
|
||||
"name": "iglootools-2",
|
||||
"repository": "test-4",
|
||||
"repository": "snowball-tools/snowball-ts-framework-template",
|
||||
"prodBranch": "main",
|
||||
"description": "test-4",
|
||||
"template": "test-4",
|
||||
"template": "webapp",
|
||||
"framework": "test-4",
|
||||
"webhooks": [],
|
||||
"icon": "",
|
||||
@ -63,10 +63,10 @@
|
||||
"ownerIndex": 0,
|
||||
"organizationIndex": 1,
|
||||
"name": "snowball-2",
|
||||
"repository": "test-5",
|
||||
"repository": "snowball-tools/snowball-ts-framework-template",
|
||||
"prodBranch": "main",
|
||||
"description": "test-5",
|
||||
"template": "test-5",
|
||||
"template": "webapp",
|
||||
"framework": "test-5",
|
||||
"webhooks": [],
|
||||
"icon": "",
|
||||
|
@ -7,6 +7,7 @@ import {
|
||||
MenuList,
|
||||
MenuItem,
|
||||
Typography,
|
||||
Avatar,
|
||||
} from '@material-tailwind/react';
|
||||
|
||||
import { relativeTimeISO } from '../../utils/time';
|
||||
@ -20,7 +21,7 @@ const ProjectCard: React.FC<ProjectCardProps> = ({ project }) => {
|
||||
return (
|
||||
<div className="bg-white border border-gray-200 rounded-lg shadow">
|
||||
<div className="flex gap-2 p-2 items-center">
|
||||
<div>{project.icon}</div>
|
||||
<Avatar variant="square" src={project.icon} />
|
||||
<div className="grow">
|
||||
<Link to={`projects/${project.id}`}>
|
||||
<Typography>{project.name}</Typography>
|
||||
@ -42,10 +43,10 @@ const ProjectCard: React.FC<ProjectCardProps> = ({ project }) => {
|
||||
</div>
|
||||
<div className="border-t-2 border-solid p-4 bg-gray-50">
|
||||
<Typography variant="small" color="gray">
|
||||
{project.latestCommit.message}
|
||||
^ {project.latestCommit.message}
|
||||
</Typography>
|
||||
<Typography variant="small" color="gray">
|
||||
{relativeTimeISO(project.latestCommit.createdAt)} on{' '}
|
||||
{relativeTimeISO(project.latestCommit.createdAt)} on ^
|
||||
{project.latestCommit.branch}
|
||||
</Typography>
|
||||
</div>
|
||||
|
@ -9,6 +9,7 @@ import {
|
||||
ListItemPrefix,
|
||||
Card,
|
||||
Typography,
|
||||
Avatar,
|
||||
} from '@material-tailwind/react';
|
||||
|
||||
import SearchBar from '../SearchBar';
|
||||
@ -86,7 +87,7 @@ const ProjectSearchBar = ({ onChange }: ProjectsSearchProps) => {
|
||||
{...getItemProps({ item, index })}
|
||||
>
|
||||
<ListItemPrefix>
|
||||
<i>^</i>
|
||||
<Avatar src={item.icon} variant="square" />
|
||||
</ListItemPrefix>
|
||||
<div>
|
||||
<Typography variant="h6" color="blue-gray">
|
||||
|
@ -32,8 +32,9 @@ const ConnectAccount = ({ onAuth: onToken }: ConnectAccountInterface) => {
|
||||
<div>
|
||||
<p>Connect to your git account</p>
|
||||
<p>
|
||||
Once connected, you can create projects by importing repositories
|
||||
under the account
|
||||
Once connected, you can import a repository from your
|
||||
<br />
|
||||
account or start with one of our templates.
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-2 flex">
|
||||
|
@ -22,12 +22,12 @@ const AssignDomainDialog = ({ open, handleOpen }: AssignDomainProps) => {
|
||||
<DialogBody>
|
||||
In order to assign a domain to your production deployments, configure it
|
||||
in the{' '}
|
||||
{/* TODO: Navigate to settings tab panel after clicking on project settings */}
|
||||
<Link to="" className="text-light-blue-800 inline">
|
||||
<Link to="../settings/domains" className="text-light-blue-800 inline">
|
||||
project settings{' '}
|
||||
</Link>
|
||||
(recommended). If you want to assign to this specific deployment,
|
||||
however, you can do so using our command-line interface:
|
||||
{/* https://github.com/rajinwonderland/react-code-blocks/issues/138 */}
|
||||
<CopyBlock
|
||||
text="snowball alias <deployment> <domain>"
|
||||
language=""
|
||||
|
@ -18,6 +18,7 @@ import DeploymentDialogBodyCard from './DeploymentDialogBodyCard';
|
||||
import AssignDomainDialog from './AssignDomainDialog';
|
||||
import { DeploymentDetails } from '../../../../types/project';
|
||||
import { useGQLClient } from '../../../../context/GQLClientContext';
|
||||
import { SHORT_COMMIT_HASH_LENGTH } from '../../../../constants';
|
||||
|
||||
interface DeployDetailsCardProps {
|
||||
deployment: DeploymentDetails;
|
||||
@ -101,12 +102,13 @@ const DeploymentDetailsCard = ({
|
||||
<div className="col-span-1">
|
||||
<Typography color="gray">^ {deployment.branch}</Typography>
|
||||
<Typography color="gray">
|
||||
^ {deployment.commitHash} {deployment.commit.message}
|
||||
^ {deployment.commitHash.substring(0, SHORT_COMMIT_HASH_LENGTH)}{' '}
|
||||
{deployment.commit.message}
|
||||
</Typography>
|
||||
</div>
|
||||
<div className="col-span-1 flex items-center">
|
||||
<Typography color="gray" className="grow">
|
||||
{relativeTimeMs(deployment.createdAt)} ^ {deployment.createdBy.name}
|
||||
^ {relativeTimeMs(deployment.createdAt)} ^ {deployment.createdBy.name}
|
||||
</Typography>
|
||||
<Menu placement="bottom-start">
|
||||
<MenuHandler>
|
||||
|
@ -4,6 +4,7 @@ import { Typography, Chip, Card } from '@material-tailwind/react';
|
||||
import { color } from '@material-tailwind/react/types/components/chip';
|
||||
import { DeploymentDetails } from '../../../../types/project';
|
||||
import { relativeTimeMs } from '../../../../utils/time';
|
||||
import { SHORT_COMMIT_HASH_LENGTH } from '../../../../constants';
|
||||
|
||||
interface DeploymentDialogBodyCardProps {
|
||||
deployment: DeploymentDetails;
|
||||
@ -31,7 +32,8 @@ const DeploymentDialogBodyCard = ({
|
||||
{deployment.url}
|
||||
</Typography>
|
||||
<Typography variant="small">
|
||||
^ {deployment.branch} ^ {deployment.commitHash}{' '}
|
||||
^ {deployment.branch} ^{' '}
|
||||
{deployment.commitHash.substring(0, SHORT_COMMIT_HASH_LENGTH)}{' '}
|
||||
{deployment.commit.message}
|
||||
</Typography>
|
||||
<Typography variant="small">
|
||||
|
@ -8,3 +8,5 @@ export const ORGANIZATION_ID = '2379cf1f-a232-4ad2-ae14-4d881131cc26';
|
||||
|
||||
export const GIT_TEMPLATE_LINK =
|
||||
'https://git.vdb.to/cerc-io/test-progressive-web-app';
|
||||
|
||||
export const SHORT_COMMIT_HASH_LENGTH = 8;
|
||||
|
@ -30,8 +30,9 @@ const Import = () => {
|
||||
<div>^</div>
|
||||
<div className="grow">{repoName}</div>
|
||||
</div>
|
||||
|
||||
<Deploy />
|
||||
<div className="w-5/6 p-6">
|
||||
<Deploy />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -1,6 +1,8 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { Outlet, useLocation, useSearchParams } from 'react-router-dom';
|
||||
|
||||
import { Avatar } from '@material-tailwind/react';
|
||||
|
||||
import Stepper from '../../../../components/Stepper';
|
||||
import templateDetails from '../../../../assets/templates.json';
|
||||
import { GIT_TEMPLATE_LINK } from '../../../../constants';
|
||||
@ -29,12 +31,12 @@ const CreateWithTemplate = () => {
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center">
|
||||
<div className="flex justify-between w-5/6 my-4 bg-gray-200 rounded-xl p-6">
|
||||
<div>^</div>
|
||||
<div className="grow">{template?.name}</div>
|
||||
<div className="flex justify-between w-5/6 my-4 bg-gray-200 rounded-xl p-6 items-center">
|
||||
<Avatar variant="square" />
|
||||
<div className="grow px-2">{template?.name}</div>
|
||||
<div>
|
||||
<a href={GIT_TEMPLATE_LINK} target="_blank" rel="noreferrer">
|
||||
cerc-io/test-progressive-web-app
|
||||
^ cerc-io/test-progressive-web-app
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react';
|
||||
import { Domain, DomainStatus } from 'gql-client';
|
||||
import { useOutletContext } from 'react-router-dom';
|
||||
|
||||
import { Typography, Button, Chip } from '@material-tailwind/react';
|
||||
import { Typography, Button, Chip, Avatar } from '@material-tailwind/react';
|
||||
|
||||
import ActivityCard from '../../../../components/projects/project/ActivityCard';
|
||||
import { relativeTimeMs } from '../../../../utils/time';
|
||||
@ -96,7 +96,7 @@ const OverviewTabPanel = () => {
|
||||
<div className="grid grid-cols-5">
|
||||
<div className="col-span-3 p-2">
|
||||
<div className="flex items-center gap-2 p-2 ">
|
||||
<div>^</div>
|
||||
<Avatar src={project.icon} variant="square" />
|
||||
<div className="grow">
|
||||
<Typography>{project.name}</Typography>
|
||||
<Typography variant="small" color="gray">
|
||||
@ -137,7 +137,7 @@ const OverviewTabPanel = () => {
|
||||
<>
|
||||
<div className="flex justify-between p-2 text-sm">
|
||||
<p>^ Source</p>
|
||||
<p>{project.deployments[0]?.branch}</p>
|
||||
<p>^ {project.deployments[0]?.branch}</p>
|
||||
</div>
|
||||
<div className="flex justify-between p-2 text-sm">
|
||||
<p>^ Deployment</p>
|
||||
|
Loading…
Reference in New Issue
Block a user