273 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			273 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
import * as plugins from './meta.plugins.js';
 | 
						|
import * as paths from '../paths.js';
 | 
						|
import * as interfaces from './meta.interfaces.js';
 | 
						|
 | 
						|
import { logger } from '../gitzone.logging.js';
 | 
						|
 | 
						|
export class Meta {
 | 
						|
  public cwd: string;
 | 
						|
  public dirName: string;
 | 
						|
  public filePaths: {
 | 
						|
    metaJson: string;
 | 
						|
    gitIgnore: string;
 | 
						|
    packageJson: string;
 | 
						|
  };
 | 
						|
 | 
						|
  constructor(cwdArg: string) {
 | 
						|
    this.cwd = cwdArg;
 | 
						|
    this.dirName = plugins.path.basename(this.cwd);
 | 
						|
    this.filePaths = {
 | 
						|
      metaJson: plugins.path.join(this.cwd, './.meta.json'),
 | 
						|
      gitIgnore: plugins.path.join(this.cwd, './.gitignore'),
 | 
						|
      packageJson: plugins.path.join(this.cwd, './package.json'),
 | 
						|
    };
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * the meta repo data
 | 
						|
   */
 | 
						|
  public metaRepoData: interfaces.IMetaRepoData;
 | 
						|
  public smartshellInstance = new plugins.smartshell.Smartshell({
 | 
						|
    executor: 'bash',
 | 
						|
  });
 | 
						|
 | 
						|
  /**
 | 
						|
   * sorts the metaRepoData
 | 
						|
   */
 | 
						|
  public async sortMetaRepoData() {
 | 
						|
    const stringifiedMetadata = plugins.smartjson.stringify(
 | 
						|
      this.metaRepoData,
 | 
						|
      [],
 | 
						|
    );
 | 
						|
    this.metaRepoData = plugins.smartjson.parse(stringifiedMetadata);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * reads the meta file from disk
 | 
						|
   */
 | 
						|
  public async readDirectory() {
 | 
						|
    await this.syncToRemote(true);
 | 
						|
    logger.log('info', `reading directory`);
 | 
						|
    const metaFileExists = plugins.smartfile.fs.fileExistsSync(
 | 
						|
      this.filePaths.metaJson,
 | 
						|
    );
 | 
						|
    if (!metaFileExists) {
 | 
						|
      throw new Error(`meta file does not exist at ${this.filePaths.metaJson}`);
 | 
						|
    }
 | 
						|
    this.metaRepoData = plugins.smartfile.fs.toObjectSync(
 | 
						|
      this.filePaths.metaJson,
 | 
						|
    );
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * generates the gitignore file and stores it on disk
 | 
						|
   */
 | 
						|
  public async generateGitignore(): Promise<string> {
 | 
						|
    await this.sortMetaRepoData();
 | 
						|
    let gitignoreString = `# ignored repo directories\n`;
 | 
						|
    gitignoreString += `.nogit/\n`;
 | 
						|
    gitignoreString += `.pnpm-store/\n`;
 | 
						|
    for (const key of Object.keys(this.metaRepoData.projects)) {
 | 
						|
      gitignoreString = `${gitignoreString}${key}\n`;
 | 
						|
    }
 | 
						|
    return gitignoreString;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * write to disk
 | 
						|
   */
 | 
						|
  public async writeToDisk() {
 | 
						|
    // write .meta.json to disk
 | 
						|
    plugins.smartfile.memory.toFsSync(
 | 
						|
      JSON.stringify(this.metaRepoData, null, 2),
 | 
						|
      this.filePaths.metaJson,
 | 
						|
    );
 | 
						|
    // write .gitignore to disk
 | 
						|
    plugins.smartfile.memory.toFsSync(
 | 
						|
      await this.generateGitignore(),
 | 
						|
      this.filePaths.gitIgnore,
 | 
						|
    );
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * push to remote
 | 
						|
   */
 | 
						|
  public async syncToRemote(gitCleanArg = false) {
 | 
						|
    logger.log('info', `syncing from origin master`);
 | 
						|
    await this.smartshellInstance.exec(
 | 
						|
      `cd ${this.cwd} && git pull origin master`,
 | 
						|
    );
 | 
						|
    if (gitCleanArg) {
 | 
						|
      logger.log('info', `cleaning the repository from old directories`);
 | 
						|
      await this.smartshellInstance.exec(`cd ${this.cwd} && git clean -fd`);
 | 
						|
    }
 | 
						|
    logger.log('info', `syncing  to remote origin master`);
 | 
						|
    await this.smartshellInstance.exec(
 | 
						|
      `cd ${this.cwd} && git push origin master`,
 | 
						|
    );
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * update the locally cloned repositories
 | 
						|
   */
 | 
						|
  public async updateLocalRepos() {
 | 
						|
    await this.syncToRemote();
 | 
						|
    const projects = plugins.smartfile.fs.toObjectSync(
 | 
						|
      this.filePaths.metaJson,
 | 
						|
    ).projects;
 | 
						|
    const preExistingFolders = plugins.smartfile.fs.listFoldersSync(this.cwd);
 | 
						|
    for (const preExistingFolderArg of preExistingFolders) {
 | 
						|
      if (
 | 
						|
        preExistingFolderArg !== '.git' &&
 | 
						|
        !Object.keys(projects).find((projectFolder) =>
 | 
						|
          projectFolder.startsWith(preExistingFolderArg),
 | 
						|
        )
 | 
						|
      ) {
 | 
						|
        const response =
 | 
						|
          await plugins.smartinteraction.SmartInteract.getCliConfirmation(
 | 
						|
            `Do you want to delete superfluous directory >>${preExistingFolderArg}<< ?`,
 | 
						|
            true,
 | 
						|
          );
 | 
						|
        if (response) {
 | 
						|
          logger.log('warn', `Deleting >>${preExistingFolderArg}<<!`);
 | 
						|
        } else {
 | 
						|
          logger.log(
 | 
						|
            'warn',
 | 
						|
            `Not deleting ${preExistingFolderArg} by request!`,
 | 
						|
          );
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    await this.readDirectory();
 | 
						|
    await this.sortMetaRepoData();
 | 
						|
    const missingRepos: string[] = [];
 | 
						|
    for (const key of Object.keys(this.metaRepoData.projects)) {
 | 
						|
      plugins.smartfile.fs.isDirectory(key)
 | 
						|
        ? logger.log('ok', `${key} -> is already cloned`)
 | 
						|
        : missingRepos.push(key);
 | 
						|
    }
 | 
						|
 | 
						|
    logger.log('info', `found ${missingRepos.length} missing repos`);
 | 
						|
    for (const missingRepo of missingRepos) {
 | 
						|
      await this.smartshellInstance.exec(
 | 
						|
        `cd ${this.cwd} && git clone ${this.metaRepoData.projects[missingRepo]} ${missingRepo}`,
 | 
						|
      );
 | 
						|
    }
 | 
						|
 | 
						|
    logger.log('info', `write changes to disk`);
 | 
						|
    await this.writeToDisk();
 | 
						|
 | 
						|
    logger.log('info', `persist changes with a git commit`);
 | 
						|
    await this.smartshellInstance.exec(
 | 
						|
      `cd ${this.cwd} && git add -A && git commit -m "updated structure"`,
 | 
						|
    );
 | 
						|
    await this.syncToRemote();
 | 
						|
 | 
						|
    // go recursive
 | 
						|
    const folders = await plugins.smartfile.fs.listFolders(this.cwd);
 | 
						|
    const childMetaRepositories: string[] = [];
 | 
						|
    for (const folder of folders) {
 | 
						|
      logger.log('info', folder);
 | 
						|
    }
 | 
						|
    console.log('Recursion still needs to be implemented');
 | 
						|
  }
 | 
						|
 | 
						|
  // project manipulation
 | 
						|
 | 
						|
  /**
 | 
						|
   * init a new meta project
 | 
						|
   */
 | 
						|
  public async initProject() {
 | 
						|
    await this.syncToRemote(true);
 | 
						|
    const fileExists = await plugins.smartfile.fs.fileExists(
 | 
						|
      this.filePaths.metaJson,
 | 
						|
    );
 | 
						|
    if (!fileExists) {
 | 
						|
      await plugins.smartfile.memory.toFs(
 | 
						|
        JSON.stringify({
 | 
						|
          projects: {},
 | 
						|
        }),
 | 
						|
        this.filePaths.metaJson,
 | 
						|
      );
 | 
						|
      logger.log(
 | 
						|
        `success`,
 | 
						|
        `created a new .meta.json in directory ${this.cwd}`,
 | 
						|
      );
 | 
						|
      await plugins.smartfile.memory.toFs(
 | 
						|
        JSON.stringify({
 | 
						|
          name: this.dirName,
 | 
						|
          version: '1.0.0',
 | 
						|
        }),
 | 
						|
        this.filePaths.packageJson,
 | 
						|
      );
 | 
						|
      logger.log(
 | 
						|
        `success`,
 | 
						|
        `created a new package.json in directory ${this.cwd}`,
 | 
						|
      );
 | 
						|
    } else {
 | 
						|
      logger.log(
 | 
						|
        `error`,
 | 
						|
        `directory ${this.cwd} already has a .metaJson file. Doing nothing.`,
 | 
						|
      );
 | 
						|
    }
 | 
						|
    await this.smartshellInstance.exec(
 | 
						|
      `cd ${this.cwd} && git add -A && git commit -m "feat(project): init meta project for ${this.dirName}"`,
 | 
						|
    );
 | 
						|
    await this.updateLocalRepos();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * adds a project
 | 
						|
   */
 | 
						|
  public async addProject(projectNameArg: string, gitUrlArg) {
 | 
						|
    await this.readDirectory();
 | 
						|
 | 
						|
    const existingProject = this.metaRepoData.projects[projectNameArg];
 | 
						|
 | 
						|
    if (existingProject) {
 | 
						|
      throw new Error(
 | 
						|
        'Project already exists! Please remove it first before adding it again.',
 | 
						|
      );
 | 
						|
    }
 | 
						|
 | 
						|
    this.metaRepoData.projects[projectNameArg] = gitUrlArg;
 | 
						|
 | 
						|
    await this.sortMetaRepoData();
 | 
						|
    await this.writeToDisk();
 | 
						|
    await this.smartshellInstance.exec(
 | 
						|
      `cd ${this.cwd} && git add -A && git commit -m "feat(project): add ${projectNameArg}"`,
 | 
						|
    );
 | 
						|
    await this.updateLocalRepos();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * removes a project
 | 
						|
   */
 | 
						|
  public async removeProject(projectNameArg: string) {
 | 
						|
    await this.readDirectory();
 | 
						|
 | 
						|
    const existingProject = this.metaRepoData.projects[projectNameArg];
 | 
						|
 | 
						|
    if (!existingProject) {
 | 
						|
      logger.log(
 | 
						|
        'error',
 | 
						|
        `Project ${projectNameArg} does not exist! So it cannot be removed`,
 | 
						|
      );
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    delete this.metaRepoData.projects[projectNameArg];
 | 
						|
 | 
						|
    logger.log('info', 'removing project from .meta.json');
 | 
						|
    await this.sortMetaRepoData();
 | 
						|
    await this.writeToDisk();
 | 
						|
 | 
						|
    logger.log('info', 'removing directory from cwd');
 | 
						|
    await plugins.smartfile.fs.remove(
 | 
						|
      plugins.path.join(paths.cwd, projectNameArg),
 | 
						|
    );
 | 
						|
    await this.updateLocalRepos();
 | 
						|
  }
 | 
						|
}
 |