diff --git a/webapp/README.md b/webapp/README.md index 6ec8c65..24f243b 100644 --- a/webapp/README.md +++ b/webapp/README.md @@ -89,6 +89,7 @@ projects: languages: # list of language codes supported in the project - sv - de + base_branch: main # optional default to 'main' ``` Start the server with `npm run dev` in `webapp`. diff --git a/webapp/src/RepoGit.ts b/webapp/src/RepoGit.ts index ffd04b1..c31e244 100644 --- a/webapp/src/RepoGit.ts +++ b/webapp/src/RepoGit.ts @@ -17,6 +17,7 @@ import { getTranslationsBySourceFile } from '@/utils/translationObjectUtil'; import { Store } from '@/store/Store'; export class RepoGit { + private readonly GIT_FETCH_TTL = 30_000; // 30 sec before fetch again private static repositories: { [name: string]: Promise; } = {}; @@ -36,7 +37,7 @@ export class RepoGit { return RepoGit.repositories[key]; } const repository = new RepoGit(spConfig); - const work = repository.checkoutBaseAndPull(); + const work = repository.fetchAndCheckoutOriginBase(); const promise = work.then(() => repository); RepoGit.repositories[key] = promise; @@ -58,26 +59,25 @@ export class RepoGit { debug(`Cloning repo: ${spConfig.repoPath} ...`); await git.clone(spConfig.cloneUrl, spConfig.repoPath); debug(`Cloned repo: ${spConfig.repoPath}`); - debug(`Checkout base branch: ${spConfig.baseBranch} ...`); - await git.checkout(spConfig.baseBranch); - debug(`Checked out base branch: ${spConfig.baseBranch}`); + debug(`Checkout base branch: ${spConfig.originBaseBranch} ...`); + await git.checkout(spConfig.originBaseBranch); + debug(`Checked out base branch: ${spConfig.originBaseBranch}`); } /** - * Checkout base branch and pull + * Fetch then checkout origin/ * @returns base branch name */ - public async checkoutBaseAndPull(): Promise { - await this.git.checkout(this.spConfig.baseBranch); - + public async fetchAndCheckoutOriginBase(): Promise { const now = new Date(); const age = now.getTime() - this.lastPullTime.getTime(); - if (age > 30000) { - // We only pull if old - await this.git.pull(); + if (age > this.GIT_FETCH_TTL) { + // We only fetch if old + await this.git.fetch(); + await this.git.checkout(this.spConfig.originBaseBranch); this.lastPullTime = now; } - return this.spConfig.baseBranch; + return this.spConfig.originBaseBranch; } public async saveLanguageFiles(projectPath: string): Promise { @@ -101,7 +101,7 @@ export class RepoGit { addFiles: string[], commitMsg: string, ): Promise { - await this.git.newBranch(branchName, this.spConfig.baseBranch); + await this.git.newBranch(branchName, this.spConfig.originBaseBranch); await this.git.add(addFiles); await this.git.commit(commitMsg); await this.git.push(branchName); diff --git a/webapp/src/actions/createPullRequest.ts b/webapp/src/actions/createPullRequest.ts index eafa597..27118f4 100644 --- a/webapp/src/actions/createPullRequest.ts +++ b/webapp/src/actions/createPullRequest.ts @@ -59,7 +59,7 @@ export default async function sendPullRequest( try { syncLock.set(repoPath, true); const repoGit = await RepoGit.getRepoGit(serverProjectConfig); - const baseBranch = await repoGit.checkoutBaseAndPull(); + const baseBranch = await repoGit.fetchAndCheckoutOriginBase(); const langFilePaths = await repoGit.saveLanguageFiles( serverProjectConfig.projectPath, ); @@ -89,7 +89,7 @@ export default async function sendPullRequest( serverProjectConfig.repo, serverProjectConfig.githubToken, ); - await repoGit.checkoutBaseAndPull(); + await repoGit.fetchAndCheckoutOriginBase(); return { branchName, pullRequestStatus: 'success', diff --git a/webapp/src/dataAccess.ts b/webapp/src/dataAccess.ts index 0518e01..ebabba4 100644 --- a/webapp/src/dataAccess.ts +++ b/webapp/src/dataAccess.ts @@ -39,7 +39,7 @@ export async function accessLanguage( await RepoGit.cloneIfNotExist(project); const repoGit = await RepoGit.getRepoGit(project); - await repoGit.checkoutBaseAndPull(); + await repoGit.fetchAndCheckoutOriginBase(); const lyraConfig = await repoGit.getLyraConfig(); const projectConfig = lyraConfig.getProjectConfigByPath(project.projectPath); const projectStore = await Store.getProjectStore(projectConfig); @@ -63,7 +63,7 @@ export async function accessLanguage( async function readProject(project: ServerProjectConfig) { await RepoGit.cloneIfNotExist(project); const repoGit = await RepoGit.getRepoGit(project); - await repoGit.checkoutBaseAndPull(); + await repoGit.fetchAndCheckoutOriginBase(); const lyraConfig = await repoGit.getLyraConfig(); const projectConfig = lyraConfig.getProjectConfigByPath(project.projectPath); const store = await Store.getProjectStore(projectConfig); diff --git a/webapp/src/utils/git/IGit.ts b/webapp/src/utils/git/IGit.ts index 0767f5a..fc61537 100644 --- a/webapp/src/utils/git/IGit.ts +++ b/webapp/src/utils/git/IGit.ts @@ -1,7 +1,7 @@ export interface IGit { clone(repoPath: string, localPath: string): Promise; - pull(): Promise; + fetch(): Promise; checkout(branch: string): Promise; diff --git a/webapp/src/utils/git/SimpleGitWrapper.ts b/webapp/src/utils/git/SimpleGitWrapper.ts index 7a74524..a18f0dc 100644 --- a/webapp/src/utils/git/SimpleGitWrapper.ts +++ b/webapp/src/utils/git/SimpleGitWrapper.ts @@ -26,8 +26,8 @@ export class SimpleGitWrapper implements IGit { await this.git.clone(repoPath, localPath); } - public async pull(): Promise { - await this.git.pull(); + public async fetch(): Promise { + await this.git.fetch(); } public async checkout(branch: string): Promise { diff --git a/webapp/src/utils/serverConfig.spec.ts b/webapp/src/utils/serverConfig.spec.ts index 11d3711..23c3325 100644 --- a/webapp/src/utils/serverConfig.spec.ts +++ b/webapp/src/utils/serverConfig.spec.ts @@ -26,6 +26,7 @@ describe('ServerConfig', () => { expect(config.projects[0].name).toEqual('foo'); expect(config.projects[0].repoPath).toEqual('/path/to/repo'); expect(config.projects[0].baseBranch).toEqual('fooBranch'); + expect(config.projects[0].originBaseBranch).toEqual('origin/fooBranch'); expect(config.projects[0].projectPath).toEqual('project'); // Note: path changed after normalization expect(config.projects[0].owner).toEqual('owner'); expect(config.projects[0].repo).toEqual('app.zetkin.org'); @@ -103,6 +104,7 @@ describe('ServerConfig', () => { const projectConfig = await ServerConfig.getProjectConfig('bar'); expect(projectConfig.repoPath).toEqual('/path/to/repo'); expect(projectConfig.baseBranch).toEqual('main'); // Note: default value when missed in config + expect(projectConfig.originBaseBranch).toEqual('origin/main'); // Note: default value when missed in config expect(projectConfig.projectPath).toEqual('project2'); }); diff --git a/webapp/src/utils/serverConfig.ts b/webapp/src/utils/serverConfig.ts index 314cc4c..a4eeba9 100644 --- a/webapp/src/utils/serverConfig.ts +++ b/webapp/src/utils/serverConfig.ts @@ -69,18 +69,21 @@ export class ServerConfig { } export class ServerProjectConfig { + public readonly originBaseBranch: string; constructor( public readonly name: string, /** absolute local path to repo */ public readonly repoPath: string, - /** following GitHub terminology target branch called base branch */ + /** following GitHub terminology target branch (typically named 'main' or 'master') called base branch */ public readonly baseBranch: string, /** relative path of project from repo_path */ public readonly projectPath: string, public readonly owner: string, public readonly repo: string, public readonly githubToken: string, - ) {} + ) { + this.originBaseBranch = `origin/${this.baseBranch}`; + } public get cloneUrl(): string { // TODO: support other provider than github