Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,10 @@ the top-level workspace directory.

The following options are available:

| Option | Description |
| --------------------------- | ------------------- |
| `-u, --use-case [use-case]` | the use case to run |
| Option | Description |
| --------------------------- | --------------------------------------------------------------------------------------------------- |
| `-u, --use-case [use-case]` | the use case to run |
| `--debug` | enable debug mode; allows to run use cases from any location, assuming the config structure matches |

For example, to run the `check-requirements` use case, execute:

Expand Down Expand Up @@ -316,6 +317,7 @@ All steps are working with a context object that is empty initially. Whenever
a use case starts, the context is filled with the following properties.
Throughout execution, steps can add or modify properties in the context object.

- `OS`: the operating system where the CLI is executed on; represents the result of [`process.platform`](https://nodejs.org/api/process.html#process_process_platform)
- `WORKSPACE_PATH`: the absolute path to the workspace
- `WORKING_DIR`: the absolute path to the working directory, see [Workspace structure](#workspace-structure)
- `SERVERS`: the list of all servers, see [Server configuration](#server-configuration)
Expand Down Expand Up @@ -395,3 +397,13 @@ After testing, the symlink can be removed via:
```bash
npm uninstall ws-ctrl -g
```

### Use cases

It is possible to test use cases directly in the workspace. To do so, the use case
should be placed in the `assets/templates/config/use-cases` directory.
The use case can then be executed via:

```bash
npm run dev -- run ./assets/templates -u <use-case-name> --debug
```
5 changes: 5 additions & 0 deletions assets/templates/config/use-cases/check-requirements.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
"name": "Check requirements",
"description": "This use case checks the requirements to develop locally.",
"steps": [
{
"type": "FORMULA",
"description": "Print OS info",
"formula": "console.log(`Running on ${OS}`)"
},
{
"type": "COMMAND",
"description": "Execute commands",
Expand Down
4 changes: 2 additions & 2 deletions dist/index.js

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions dist/templates/config/use-cases/check-requirements.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
"name": "Check requirements",
"description": "This use case checks the requirements to develop locally.",
"steps": [
{
"type": "FORMULA",
"description": "Print OS info",
"formula": "console.log(`Running on ${OS}`)"
},
{
"type": "COMMAND",
"description": "Execute commands",
Expand Down
4 changes: 2 additions & 2 deletions src/commands/init/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ export async function init(
const config = initConfig(workspacePath, organization, templatesRepository);

// copy templates from package/repo to workspace
const templatesAccess = TemplatesAccess.create(config);
const templatesAccess = TemplatesAccess.create(config.store);
await templatesAccess.initWorkspace();

const useCaseRunner = UseCaseRunner.create(config, templatesAccess);
const useCaseRunner = UseCaseRunner.create(templatesAccess);
await useCaseRunner.run('init');
}
7 changes: 5 additions & 2 deletions src/commands/run/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,20 @@ const useCaseOption = new Option(
'-u, --use-case [use-case]',
'execute the use case with the given name'
);
const debugOption = new Option('--debug', 'enable debug mode');

interface RunActionOptions {
useCase: OptionInput;
debug: boolean;
}

export async function runAction(
workspacePathRaw: string,
options: RunActionOptions
) {
const config = await loadWorkspaceConfig(workspacePathRaw);
const config = await loadWorkspaceConfig(workspacePathRaw, options.debug);
const templatesAccess = TemplatesAccess.create(config);
const useCaseRunner = UseCaseRunner.create(config, templatesAccess);
const useCaseRunner = UseCaseRunner.create(templatesAccess);
const useCaseRepository = UseCasesRepository.create(templatesAccess);

const useCases = await useCaseRepository.loadUseCases('INITIAL');
Expand All @@ -43,4 +45,5 @@ export const run = new Command()
.description('run a use case in the workspace')
.addArgument(defaultWorkspacePathArgument)
.addOption(useCaseOption)
.addOption(debugOption)
.action(runAction);
2 changes: 1 addition & 1 deletion src/commands/sync/sync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ async function syncAction(workspacePathRaw: string): Promise<void> {
const templatesAccess = TemplatesAccess.create(config);
await templatesAccess.syncTemplates();

const useCaseRunner = UseCaseRunner.create(config, templatesAccess);
const useCaseRunner = UseCaseRunner.create(templatesAccess);
await useCaseRunner.run('sync');
}

Expand Down
41 changes: 29 additions & 12 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import Conf from 'conf';
import {
getWorkspacePathIdentifier,
isExistingWorkspace,
logger,
resolveWorkspacePath,
} from './utils/index.js';

interface Config {
export interface Config {
workspacePath: string;
organization: string;
templatesRepository: string | null;
Expand All @@ -27,6 +28,23 @@ const schema = {

export let config: WorkspaceConfig;

function loadConfig(workspacePath: string): WorkspaceConfig {
config = new Conf<Config>({
schema,
cwd: resolveWorkspacePath(workspacePath),
configName: getWorkspacePathIdentifier(workspacePath),
});
return config;
}

function loadBlankConfig(workspacePath: string): Config {
return {
workspacePath: resolveWorkspacePath(workspacePath),
organization: 'none',
templatesRepository: null,
};
}

export function initConfig(
workspacePath: string,
organization: string,
Expand All @@ -39,20 +57,19 @@ export function initConfig(
return config;
}

export function loadConfig(workspacePath: string): WorkspaceConfig {
config = new Conf<Config>({
schema,
cwd: resolveWorkspacePath(workspacePath),
configName: getWorkspacePathIdentifier(workspacePath),
});
return config;
}

export async function loadWorkspaceConfig(workspacePathRaw: string) {
export async function loadWorkspaceConfig(
workspacePathRaw: string,
debug: boolean = false
): Promise<Config> {
const workspacePath = workspacePathRaw.trim();
if (debug) {
logger.log('Running within non-workspace directory...');
return loadBlankConfig(workspacePath);
}

const existingWorkspace = await isExistingWorkspace(workspacePath);
if (!existingWorkspace) {
throw new Error(`The given path is no valid workspace.`);
}
return loadConfig(workspacePath);
return loadConfig(workspacePath).store;
}
19 changes: 8 additions & 11 deletions src/services/access/templates-access.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { join } from 'node:path';
import { WorkspaceConfig } from '../../config.js';
import { Config } from '../../config.js';
import { copyDirectory, gitUpdate, logger } from '../../utils/index.js';
import {
DIR_CONFIG,
Expand All @@ -19,11 +19,11 @@ enum Location {
}

export class TemplatesAccess {
static create(config: WorkspaceConfig): TemplatesAccess {
static create(config: Config): TemplatesAccess {
return new TemplatesAccess(config);
}

constructor(private readonly config: WorkspaceConfig) {}
constructor(private readonly config: Config) {}

getPackageTemplatesDir(): string {
return join(this.#getBaseDir(Location.PACKAGE), DIR_TEMPLATES);
Expand Down Expand Up @@ -68,19 +68,19 @@ export class TemplatesAccess {
}

getWorkspacePath(): string {
return this.config.get('workspacePath');
return this.config.workspacePath;
}

getTemplatesRepository(): string | null {
return this.config.get('templatesRepository');
return this.config.templatesRepository;
}

createRepositoryUrl(organization: string, name: string): string {
createRepositoryUrl(name: string): string {
// TODO:
// - add support for HTTPS URLs when provided in config
// - add support for different repository hosts
// - add support for different tenants
return `git@bitbucket.org:${organization}/${name}.git`;
return `git@bitbucket.org:${this.config.organization}/${name}.git`;
}

async initWorkspace(): Promise<void> {
Expand All @@ -102,10 +102,7 @@ export class TemplatesAccess {
// sync the templates from the repository if configured
const templatesRepository = this.getTemplatesRepository();
if (templatesRepository) {
const url = this.createRepositoryUrl(
this.config.get('organization'),
templatesRepository
);
const url = this.createRepositoryUrl(templatesRepository);
logger.log(`Syncing templates from repository ${url}...`);

await gitUpdate(url, this.getWorkspacePath(), this.getGitTemplatesDir());
Expand Down
4 changes: 2 additions & 2 deletions src/services/repositories.repository.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ describe('RepositoriesRepository', () => {
let sut: RepositoriesRepository;

beforeEach(() => {
const config = initConfig(path, 'acme', null);
const config = initConfig(path, 'acme', null).store;

sut = RepositoriesRepository.create(config, TemplatesAccess.create(config));
sut = RepositoriesRepository.create(TemplatesAccess.create(config));
});

it('should load repositories; empty list', async () => {
Expand Down
15 changes: 4 additions & 11 deletions src/services/repositories.repository.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
import { WorkspaceConfig } from '../config.js';
import { Repository } from '../types/index.js';
import { loadFilesFromDirectory } from '../utils/index.js';
import { TemplatesAccess } from './access/index.js';

export class RepositoriesRepository {
static create(config: WorkspaceConfig, templatesAccess: TemplatesAccess) {
return new RepositoriesRepository(config, templatesAccess);
static create(templatesAccess: TemplatesAccess) {
return new RepositoriesRepository(templatesAccess);
}

constructor(
private readonly config: WorkspaceConfig,
private readonly templatesAccess: TemplatesAccess
) {}
constructor(private readonly templatesAccess: TemplatesAccess) {}

async loadRepositories(): Promise<Required<Repository>[]> {
return this.loadFiles().then(repositories =>
Expand All @@ -31,10 +27,7 @@ export class RepositoriesRepository {
alias: repository.alias || repository.name,
url:
repository.url ||
this.templatesAccess.createRepositoryUrl(
this.config.get('organization'),
repository.name
),
this.templatesAccess.createRepositoryUrl(repository.name),
attributes: {
type: 'UNKNOWN',
...repository.attributes,
Expand Down
2 changes: 1 addition & 1 deletion src/services/servers.repository.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ describe('ServersRepository', () => {
let sut: ServersRepository;

beforeEach(() => {
const config = initConfig(path, 'acme', null);
const config = initConfig(path, 'acme', null).store;

sut = ServersRepository.create(TemplatesAccess.create(config));
});
Expand Down
2 changes: 1 addition & 1 deletion src/services/use-cases.repository.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ describe('UseCasesRepository', () => {
let sut: UseCasesRepository;

beforeEach(() => {
const config = initConfig(path, 'acme', null);
const config = initConfig(path, 'acme', null).store;

sut = UseCasesRepository.create(TemplatesAccess.create(config));
});
Expand Down
7 changes: 4 additions & 3 deletions src/services/use-cases/context-creator.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { WorkspaceConfig } from '../../config.js';
import { Context } from '../../types/index.js';
import { OS } from '../../utils/index.js';
import { TemplatesAccess } from '../access/index.js';
import { RepositoriesRepository } from '../repositories.repository.js';
import { ServersRepository } from '../servers.repository.js';

export class ContextCreator {
static create(config: WorkspaceConfig, templatesAccess: TemplatesAccess) {
static create(templatesAccess: TemplatesAccess) {
return new ContextCreator(
templatesAccess,
ServersRepository.create(templatesAccess),
RepositoriesRepository.create(config, templatesAccess)
RepositoriesRepository.create(templatesAccess)
);
}

Expand All @@ -26,6 +26,7 @@ export class ContextCreator {
WORKING_DIR: this.templatesAccess.getWorkingDir(),
SERVERS: await this.serversRepository.loadServers(),
REPOSITORIES: await this.repositoriesRepository.loadRepositories(),
OS,
};
}
}
8 changes: 2 additions & 6 deletions src/services/use-cases/use-case-runner.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,10 @@ describe('UseCaseRunner', () => {
let sut: UseCaseRunner;

beforeEach(() => {
const config = initConfig(path, 'acme', null);
const config = initConfig(path, 'acme', null).store;

scriptExecutor = ScriptExecutor.create();
sut = UseCaseRunner.create(
config,
TemplatesAccess.create(config),
scriptExecutor
);
sut = UseCaseRunner.create(TemplatesAccess.create(config), scriptExecutor);
});

afterEach(() => {
Expand Down
4 changes: 1 addition & 3 deletions src/services/use-cases/use-case-runner.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { WorkspaceConfig } from '../../config.js';
import { Context, UseCase, UseCaseStep } from '../../types/index.js';
import {
bold,
Expand All @@ -16,14 +15,13 @@ import { executorMapping } from './executors/index.js';

export class UseCaseRunner {
static create(
config: WorkspaceConfig,
templatesAccess: TemplatesAccess,
scriptExecutor?: ScriptExecutor
): UseCaseRunner {
return new UseCaseRunner(
templatesAccess,
UseCasesRepository.create(templatesAccess),
ContextCreator.create(config, templatesAccess),
ContextCreator.create(templatesAccess),
scriptExecutor || ScriptExecutor.create()
);
}
Expand Down
2 changes: 2 additions & 0 deletions src/utils/processes.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { execSync } from 'node:child_process';

export const OS = process.platform;

export async function execCommand(command: string, cwd: string) {
return execSync(command, {
cwd,
Expand Down