feat: 为 pipeline 添加运行环境,并用于配置文件的发布。
This commit is contained in:
parent
3ba8fc9759
commit
6b9f846154
1
.eslintcache
Normal file
1
.eslintcache
Normal file
File diff suppressed because one or more lines are too long
6
package-lock.json
generated
6
package-lock.json
generated
@ -76,7 +76,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"../configuration": {
|
"../configuration": {
|
||||||
"version": "1.0.0",
|
"version": "0.0.1",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"debug": "^4.3.2",
|
"debug": "^4.3.2",
|
||||||
@ -90,7 +90,7 @@
|
|||||||
"@types/node": "^14.17.17",
|
"@types/node": "^14.17.17",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"ts-node": "^10.2.1",
|
"ts-node": "^10.2.1",
|
||||||
"typescript": "^4.4.3"
|
"typescript": "^4.4.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@angular-devkit/core": {
|
"node_modules/@angular-devkit/core": {
|
||||||
@ -23187,7 +23187,7 @@
|
|||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"ts-node": "^10.2.1",
|
"ts-node": "^10.2.1",
|
||||||
"typescript": "^4.4.3"
|
"typescript": "^4.4.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"consola": {
|
"consola": {
|
||||||
|
@ -65,7 +65,7 @@ import { ConfigurationsModule } from './configurations/configurations.module';
|
|||||||
playground: true,
|
playground: true,
|
||||||
autoSchemaFile: true,
|
autoSchemaFile: true,
|
||||||
installSubscriptionHandlers: true,
|
installSubscriptionHandlers: true,
|
||||||
context: ({ req, connection, ...args }) => {
|
context: ({ req, connection }) => {
|
||||||
return connection ? { req: connection.context } : { req };
|
return connection ? { req: connection.context } : { req };
|
||||||
},
|
},
|
||||||
subscriptions: {
|
subscriptions: {
|
||||||
|
@ -13,9 +13,9 @@ export class ApplicationException extends Error {
|
|||||||
this.error = message.error;
|
this.error = message.error;
|
||||||
this.message = message.message as any;
|
this.message = message.message as any;
|
||||||
} else if (typeof message === 'string') {
|
} else if (typeof message === 'string') {
|
||||||
super((message as unknown) as any);
|
super(message as unknown as any);
|
||||||
} else {
|
} else {
|
||||||
super((message as unknown) as any);
|
super(message as unknown as any);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,12 +19,16 @@ export class HttpExceptionFilter implements ExceptionFilter {
|
|||||||
case 'graphql': {
|
case 'graphql': {
|
||||||
const errorName = exception.message;
|
const errorName = exception.message;
|
||||||
const extensions: Record<string, any> = {};
|
const extensions: Record<string, any> = {};
|
||||||
const err = exception.getResponse();
|
const err = exception.getResponse() as any;
|
||||||
if (typeof err === 'string') {
|
if (typeof err === 'string') {
|
||||||
extensions.message = err;
|
extensions.message = err;
|
||||||
} else {
|
} else {
|
||||||
Object.assign(extensions, (err as any).extension);
|
Object.assign(extensions, err.extension);
|
||||||
extensions.message = (err as any).message;
|
if (typeof err.message === 'string') {
|
||||||
|
extensions.message = err.message;
|
||||||
|
} else {
|
||||||
|
extensions.message = err.error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
extensions.error = errorName;
|
extensions.error = errorName;
|
||||||
this.logger.error(extensions);
|
this.logger.error(extensions);
|
||||||
|
@ -126,6 +126,7 @@ export class BaseDbService<Entity extends AppBaseEntity> extends TypeormHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
async canYouRemoveWithIds(ids: string[]): Promise<void> {
|
async canYouRemoveWithIds(ids: string[]): Promise<void> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { JwtService } from '@nestjs-lib/auth';
|
||||||
import { Test, TestingModule } from '@nestjs/testing';
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
import { ConfigurationsResolver } from './configurations.resolver';
|
import { ConfigurationsResolver } from './configurations.resolver';
|
||||||
import { ConfigurationsService } from './configurations.service';
|
import { ConfigurationsService } from './configurations.service';
|
||||||
@ -8,10 +9,15 @@ describe('ConfigurationsResolver', () => {
|
|||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
providers: [
|
providers: [
|
||||||
|
ConfigurationsResolver,
|
||||||
{
|
{
|
||||||
provide: ConfigurationsService,
|
provide: ConfigurationsService,
|
||||||
useValue: {},
|
useValue: {},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: JwtService,
|
||||||
|
useValue: {},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}).compile();
|
}).compile();
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ import { Test, TestingModule } from '@nestjs/testing';
|
|||||||
import { ConfigurationsService } from './configurations.service';
|
import { ConfigurationsService } from './configurations.service';
|
||||||
import { Configuration } from './entities/configuration.entity';
|
import { Configuration } from './entities/configuration.entity';
|
||||||
import { IsNull } from 'typeorm';
|
import { IsNull } from 'typeorm';
|
||||||
|
import { Etcd3 } from 'etcd3';
|
||||||
|
|
||||||
describe('ConfigurationsService', () => {
|
describe('ConfigurationsService', () => {
|
||||||
let service: ConfigurationsService;
|
let service: ConfigurationsService;
|
||||||
@ -15,6 +16,10 @@ describe('ConfigurationsService', () => {
|
|||||||
provide: getRepositoryToken(Configuration),
|
provide: getRepositoryToken(Configuration),
|
||||||
useValue: {},
|
useValue: {},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: Etcd3,
|
||||||
|
useValue: {},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}).compile();
|
}).compile();
|
||||||
|
|
||||||
@ -28,7 +33,7 @@ describe('ConfigurationsService', () => {
|
|||||||
describe('findOneByConditions', () => {
|
describe('findOneByConditions', () => {
|
||||||
it('should select by projectId only', async () => {
|
it('should select by projectId only', async () => {
|
||||||
const entity = new Configuration();
|
const entity = new Configuration();
|
||||||
const findOne = jest.fn((_) => Promise.resolve(entity));
|
const findOne = jest.fn<any, [any]>(() => Promise.resolve(entity));
|
||||||
service['repository'].findOne = findOne;
|
service['repository'].findOne = findOne;
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
|
@ -26,7 +26,7 @@ export class ConfigurationsService extends BaseDbService<Configuration> {
|
|||||||
entity = this.repository.create(dto);
|
entity = this.repository.create(dto);
|
||||||
}
|
}
|
||||||
entity = await this.repository.save(entity);
|
entity = await this.repository.save(entity);
|
||||||
this.etcd.put(`share/config/${entity.id}`).value(entity.content);
|
await this.syncToEtcd(entity);
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,4 +36,20 @@ export class ConfigurationsService extends BaseDbService<Configuration> {
|
|||||||
}
|
}
|
||||||
return await this.repository.findOne(dto);
|
return await this.repository.findOne(dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async syncToEtcd({ pipelineId, id }: { pipelineId?: string; id?: string }) {
|
||||||
|
const config = await this.repository.findOneOrFail({
|
||||||
|
where: { pipelineId, id },
|
||||||
|
relations: ['pipeline', 'project'],
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.etcd
|
||||||
|
.put(`share/config/${config.id}`)
|
||||||
|
.value(config.content)
|
||||||
|
.exec();
|
||||||
|
await this.etcd
|
||||||
|
.put(`share/config/${config.pipeline.environment}/${config.project.name}`)
|
||||||
|
.value(config.content)
|
||||||
|
.exec();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Field, InputType } from '@nestjs/graphql';
|
import { InputType } from '@nestjs/graphql';
|
||||||
import { PipelineUnits } from '../enums/pipeline-units.enum';
|
import { PipelineUnits } from '../enums/pipeline-units.enum';
|
||||||
|
|
||||||
@InputType()
|
@InputType()
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Field, InputType, Int, ObjectType } from '@nestjs/graphql';
|
import { Field, InputType, Int, ObjectType } from '@nestjs/graphql';
|
||||||
import { Type } from 'class-transformer';
|
import { Type } from 'class-transformer';
|
||||||
import { IsInstance, isInstance, ValidateNested } from 'class-validator';
|
import { IsInstance, ValidateNested } from 'class-validator';
|
||||||
import { WorkUnit } from './work-unit.model';
|
import { WorkUnit } from './work-unit.model';
|
||||||
|
|
||||||
@InputType('WorkUnitMetadataInput')
|
@InputType('WorkUnitMetadataInput')
|
||||||
|
@ -35,15 +35,6 @@ export class PipelineTasksResolver {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscription(() => PipelineTask, {
|
|
||||||
resolve: (value) => {
|
|
||||||
return value;
|
|
||||||
},
|
|
||||||
})
|
|
||||||
async pipelineTaskChanged(@Args('id') id: string) {
|
|
||||||
// return await this.service.watchTaskUpdated(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Query(() => [PipelineTask])
|
@Query(() => [PipelineTask])
|
||||||
async listPipelineTaskByPipelineId(@Args('pipelineId') pipelineId: string) {
|
async listPipelineTaskByPipelineId(@Args('pipelineId') pipelineId: string) {
|
||||||
return await this.service.listTasksByPipelineId(pipelineId);
|
return await this.service.listTasksByPipelineId(pipelineId);
|
||||||
|
@ -12,7 +12,6 @@ describe('PipelineTasksService', () => {
|
|||||||
let service: PipelineTasksService;
|
let service: PipelineTasksService;
|
||||||
let module: TestingModule;
|
let module: TestingModule;
|
||||||
let taskRepository: Repository<PipelineTask>;
|
let taskRepository: Repository<PipelineTask>;
|
||||||
let pipelineRepository: Repository<Pipeline>;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
module = await Test.createTestingModule({
|
module = await Test.createTestingModule({
|
||||||
@ -43,7 +42,6 @@ describe('PipelineTasksService', () => {
|
|||||||
|
|
||||||
service = module.get<PipelineTasksService>(PipelineTasksService);
|
service = module.get<PipelineTasksService>(PipelineTasksService);
|
||||||
taskRepository = module.get(getRepositoryToken(PipelineTask));
|
taskRepository = module.get(getRepositoryToken(PipelineTask));
|
||||||
pipelineRepository = module.get(getRepositoryToken(Pipeline));
|
|
||||||
jest
|
jest
|
||||||
.spyOn(taskRepository, 'save')
|
.spyOn(taskRepository, 'save')
|
||||||
.mockImplementation(async (data: any) => data);
|
.mockImplementation(async (data: any) => data);
|
||||||
|
@ -4,7 +4,6 @@ import { PipelineTask } from './pipeline-task.entity';
|
|||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
import { CreatePipelineTaskInput } from './dtos/create-pipeline-task.input';
|
import { CreatePipelineTaskInput } from './dtos/create-pipeline-task.input';
|
||||||
import { Pipeline } from '../pipelines/pipeline.entity';
|
import { Pipeline } from '../pipelines/pipeline.entity';
|
||||||
import debug from 'debug';
|
|
||||||
import { AmqpConnection, RabbitRPC } from '@golevelup/nestjs-rabbitmq';
|
import { AmqpConnection, RabbitRPC } from '@golevelup/nestjs-rabbitmq';
|
||||||
import {
|
import {
|
||||||
EXCHANGE_PIPELINE_TASK_TOPIC,
|
EXCHANGE_PIPELINE_TASK_TOPIC,
|
||||||
@ -19,8 +18,6 @@ import { InjectPinoLogger, PinoLogger } from 'nestjs-pino';
|
|||||||
import { getAppInstanceRouteKey } from '../commons/utils/rabbit-mq';
|
import { getAppInstanceRouteKey } from '../commons/utils/rabbit-mq';
|
||||||
import { ROUTE_PIPELINE_TASK_KILL } from './pipeline-tasks.constants';
|
import { ROUTE_PIPELINE_TASK_KILL } from './pipeline-tasks.constants';
|
||||||
|
|
||||||
const log = debug('fennec:pipeline-tasks:service');
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PipelineTasksService {
|
export class PipelineTasksService {
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -28,4 +28,8 @@ export class CreatePipelineInput {
|
|||||||
@ValidateNested()
|
@ValidateNested()
|
||||||
@IsInstance(WorkUnitMetadata)
|
@IsInstance(WorkUnitMetadata)
|
||||||
workUnitMetadata: WorkUnitMetadata;
|
workUnitMetadata: WorkUnitMetadata;
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
@MaxLength(100)
|
||||||
|
environment: string;
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ export class Pipeline extends AppBaseEntity {
|
|||||||
@Column()
|
@Column()
|
||||||
projectId: string;
|
projectId: string;
|
||||||
|
|
||||||
@Column({ comment: 'eg: remotes/origin/master' })
|
@Column({ comment: 'E.g., remotes/origin/master' })
|
||||||
branch: string;
|
branch: string;
|
||||||
|
|
||||||
@Column()
|
@Column()
|
||||||
@ -20,4 +20,7 @@ export class Pipeline extends AppBaseEntity {
|
|||||||
|
|
||||||
@Column({ type: 'jsonb' })
|
@Column({ type: 'jsonb' })
|
||||||
workUnitMetadata: WorkUnitMetadata;
|
workUnitMetadata: WorkUnitMetadata;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
environment: string;
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,11 @@ import { Test, TestingModule } from '@nestjs/testing';
|
|||||||
import { PipelinesService } from './pipelines.service';
|
import { PipelinesService } from './pipelines.service';
|
||||||
import { Pipeline } from './pipeline.entity';
|
import { Pipeline } from './pipeline.entity';
|
||||||
import { getRepositoryToken } from '@nestjs/typeorm';
|
import { getRepositoryToken } from '@nestjs/typeorm';
|
||||||
import { Repository } from 'typeorm';
|
|
||||||
import { Project } from '../projects/project.entity';
|
import { Project } from '../projects/project.entity';
|
||||||
import { AmqpConnection } from '@golevelup/nestjs-rabbitmq';
|
import { AmqpConnection } from '@golevelup/nestjs-rabbitmq';
|
||||||
|
|
||||||
describe('PipelinesService', () => {
|
describe('PipelinesService', () => {
|
||||||
let service: PipelinesService;
|
let service: PipelinesService;
|
||||||
let repository: Repository<Pipeline>;
|
|
||||||
let pipeline: Pipeline;
|
let pipeline: Pipeline;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
@ -40,7 +38,6 @@ describe('PipelinesService', () => {
|
|||||||
}).compile();
|
}).compile();
|
||||||
|
|
||||||
service = module.get<PipelinesService>(PipelinesService);
|
service = module.get<PipelinesService>(PipelinesService);
|
||||||
repository = module.get(getRepositoryToken(Pipeline));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be defined', () => {
|
it('should be defined', () => {
|
||||||
|
@ -19,7 +19,9 @@ import { plainToClass } from 'class-transformer';
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PipelinesService extends BaseDbService<Pipeline> {
|
export class PipelinesService extends BaseDbService<Pipeline> {
|
||||||
readonly uniqueFields: Array<Array<keyof Pipeline>> = [['projectId', 'name']];
|
readonly uniqueFields: Array<Array<keyof Pipeline>> = [
|
||||||
|
['projectId', 'name', 'environment'],
|
||||||
|
];
|
||||||
constructor(
|
constructor(
|
||||||
@InjectRepository(Pipeline)
|
@InjectRepository(Pipeline)
|
||||||
readonly repository: Repository<Pipeline>,
|
readonly repository: Repository<Pipeline>,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { ObjectType } from '@nestjs/graphql';
|
import { ObjectType } from '@nestjs/graphql';
|
||||||
import { Entity, Column, DeleteDateColumn } from 'typeorm';
|
import { Entity, Column } from 'typeorm';
|
||||||
import { AppBaseEntity } from '../commons/entities/app-base-entity';
|
import { AppBaseEntity } from '../commons/entities/app-base-entity';
|
||||||
|
|
||||||
@ObjectType()
|
@ObjectType()
|
||||||
|
@ -1,10 +1,5 @@
|
|||||||
import { ObjectType, Field } from '@nestjs/graphql';
|
import { ObjectType, Field } from '@nestjs/graphql';
|
||||||
import {
|
import { BranchSummaryBranch } from 'simple-git';
|
||||||
LogResult,
|
|
||||||
DefaultLogFields,
|
|
||||||
BranchSummary,
|
|
||||||
BranchSummaryBranch,
|
|
||||||
} from 'simple-git';
|
|
||||||
|
|
||||||
@ObjectType()
|
@ObjectType()
|
||||||
export class Branch implements BranchSummaryBranch {
|
export class Branch implements BranchSummaryBranch {
|
||||||
|
@ -161,7 +161,7 @@ describe('ReposService', () => {
|
|||||||
const project = new Project();
|
const project = new Project();
|
||||||
const pipeline = new Pipeline();
|
const pipeline = new Pipeline();
|
||||||
pipeline.branch = 'test';
|
pipeline.branch = 'test';
|
||||||
const fetch = jest.fn((_: any) => Promise.resolve());
|
const fetch = jest.fn<any, [any]>(() => Promise.resolve());
|
||||||
pipeline.project = project;
|
pipeline.project = project;
|
||||||
const getGit = jest.spyOn(service, 'getGit').mockImplementation(() =>
|
const getGit = jest.spyOn(service, 'getGit').mockImplementation(() =>
|
||||||
Promise.resolve({
|
Promise.resolve({
|
||||||
@ -182,7 +182,7 @@ describe('ReposService', () => {
|
|||||||
const project = new Project();
|
const project = new Project();
|
||||||
const pipeline = new Pipeline();
|
const pipeline = new Pipeline();
|
||||||
pipeline.branch = 'test';
|
pipeline.branch = 'test';
|
||||||
const fetch = jest.fn((_: any) => Promise.resolve());
|
const fetch = jest.fn<any, [any]>(() => Promise.resolve());
|
||||||
pipeline.project = project;
|
pipeline.project = project;
|
||||||
const getGit = jest
|
const getGit = jest
|
||||||
.spyOn(service, 'getGit')
|
.spyOn(service, 'getGit')
|
||||||
@ -196,7 +196,7 @@ describe('ReposService', () => {
|
|||||||
const project = new Project();
|
const project = new Project();
|
||||||
const pipeline = new Pipeline();
|
const pipeline = new Pipeline();
|
||||||
pipeline.branch = 'test';
|
pipeline.branch = 'test';
|
||||||
const fetch = jest.fn((_: any) => Promise.reject('error'));
|
const fetch = jest.fn<any, [any]>(() => Promise.reject('error'));
|
||||||
pipeline.project = project;
|
pipeline.project = project;
|
||||||
const getGit = jest.spyOn(service, 'getGit').mockImplementation(() =>
|
const getGit = jest.spyOn(service, 'getGit').mockImplementation(() =>
|
||||||
Promise.resolve({
|
Promise.resolve({
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { MiddlewareConsumer, Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
import { PipelineTasksModule } from '../pipeline-tasks/pipeline-tasks.module';
|
import { PipelineTasksModule } from '../pipeline-tasks/pipeline-tasks.module';
|
||||||
import { GiteaWebhooksController } from './gitea-webhooks.controller';
|
import { GiteaWebhooksController } from './gitea-webhooks.controller';
|
||||||
@ -10,5 +10,4 @@ import { WebhooksService } from './webhooks.service';
|
|||||||
controllers: [GiteaWebhooksController],
|
controllers: [GiteaWebhooksController],
|
||||||
providers: [WebhooksService],
|
providers: [WebhooksService],
|
||||||
})
|
})
|
||||||
export class WebhooksModule {
|
export class WebhooksModule {}
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user