renovate 40.26.3 → 40.27.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -239,5 +239,6 @@ export interface PlatformScm {
239
239
  checkoutBranch(branchName: string): Promise<LongCommitSha>;
240
240
  mergeToLocal(branchName: string): Promise<void>;
241
241
  mergeAndPush(branchName: string): Promise<void>;
242
+ syncForkWithUpstream?(baseBranch: string): Promise<void>;
242
243
  }
243
244
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../lib/modules/platform/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { MergeStrategy } from '../../config/types';\nimport type { BranchStatus, HostRule, VulnerabilityAlert } from '../../types';\nimport type { CommitFilesConfig, LongCommitSha } from '../../util/git/types';\n\ntype VulnerabilityKey = string;\ntype VulnerabilityRangeKey = string;\ntype VulnerabilityPatch = string;\nexport type AggregatedVulnerabilities = Record<\n VulnerabilityKey,\n Record<VulnerabilityRangeKey, VulnerabilityPatch | null>\n>;\n\nexport interface PlatformParams {\n endpoint?: string;\n token?: string;\n username?: string;\n password?: string;\n gitAuthor?: string;\n}\n\nexport interface PlatformResult {\n endpoint: string;\n renovateUsername?: string;\n token?: string;\n gitAuthor?: string;\n /*\n * return these only if _additional_ rules/hosts are required\n */\n hostRules?: HostRule[];\n}\n\nexport interface RepoResult {\n defaultBranch: string;\n isFork: boolean;\n repoFingerprint: string;\n}\n\nexport type GitUrlOption = 'default' | 'ssh' | 'endpoint';\n\nexport interface RepoParams {\n repository: string;\n endpoint?: string;\n gitUrl?: GitUrlOption;\n forkCreation?: boolean;\n forkOrg?: string;\n forkToken?: string;\n forkProcessing?: 'enabled' | 'disabled';\n renovateUsername?: string;\n cloneSubmodules?: boolean;\n cloneSubmodulesFilter?: string[];\n ignorePrAuthor?: boolean;\n bbUseDevelopmentBranch?: boolean;\n includeMirrors?: boolean;\n}\n\nexport interface PrDebugData {\n createdInVer: string;\n updatedInVer: string;\n targetBranch: string;\n labels?: string[];\n}\n\nexport interface PrBodyStruct {\n hash: string;\n rawConfigHash?: string;\n rebaseRequested?: boolean;\n debugData?: PrDebugData;\n}\n\n/**\n *\n */\nexport interface Pr {\n bodyStruct?: PrBodyStruct;\n sourceBranch: string;\n cannotMergeReason?: string; // for reflecting platform policies which may prevent merging\n createdAt?: string;\n closedAt?: string;\n hasAssignees?: boolean;\n labels?: string[];\n number: number;\n reviewers?: string[];\n sha?: LongCommitSha;\n sourceRepo?: string;\n state: string;\n targetBranch?: string;\n title: string;\n isDraft?: boolean;\n}\n\n/**\n * TODO: Proper typing\n */\nexport interface Issue {\n body?: string;\n number?: number;\n state?: string;\n title?: string;\n}\nexport interface PlatformPrOptions {\n autoApprove?: boolean;\n automergeStrategy?: MergeStrategy;\n azureWorkItemId?: number;\n bbUseDefaultReviewers?: boolean;\n bbAutoResolvePrTasks?: boolean;\n gitLabIgnoreApprovals?: boolean;\n usePlatformAutomerge?: boolean;\n forkModeDisallowMaintainerEdits?: boolean;\n}\n\nexport interface CreatePRConfig {\n sourceBranch: string;\n targetBranch: string;\n prTitle: string;\n prBody: string;\n labels?: string[] | null;\n platformPrOptions?: PlatformPrOptions;\n draftPR?: boolean;\n milestone?: number;\n}\nexport interface UpdatePrConfig {\n number: number;\n platformPrOptions?: PlatformPrOptions;\n prTitle: string;\n prBody?: string;\n state?: 'open' | 'closed';\n targetBranch?: string;\n\n /**\n * This field allows for label management and is designed to\n * accommodate the different label update methods on various platforms.\n *\n * - For Gitea, labels are updated by replacing the entire labels array.\n * - In the case of GitHub and GitLab, specific endpoints exist\n * for adding and removing labels.\n */\n labels?: string[] | null;\n\n /**\n * Specifies an array of labels to be added.\n * @see {@link labels}\n */\n addLabels?: string[] | null;\n\n /**\n * Specifies an array of labels to be removed.\n * @see {@link labels}\n */\n removeLabels?: string[] | null;\n}\nexport interface ReattemptPlatformAutomergeConfig {\n number: number;\n platformPrOptions?: PlatformPrOptions;\n}\nexport interface EnsureIssueConfig {\n title: string;\n reuseTitle?: string;\n body: string;\n labels?: string[];\n once?: boolean;\n shouldReOpen?: boolean;\n confidential?: boolean;\n}\nexport interface BranchStatusConfig {\n branchName: string;\n context: string;\n description: string;\n state: BranchStatus;\n url?: string;\n}\nexport interface FindPRConfig {\n branchName: string;\n prTitle?: string | null;\n state?: 'open' | 'closed' | '!open' | 'all';\n refreshCache?: boolean;\n targetBranch?: string | null;\n includeOtherAuthors?: boolean;\n}\nexport interface MergePRConfig {\n branchName?: string;\n id: number;\n strategy?: MergeStrategy;\n}\nexport interface EnsureCommentConfig {\n number: number;\n topic: string | null;\n content: string;\n}\n\nexport interface EnsureCommentRemovalConfigByTopic {\n type: 'by-topic';\n number: number;\n topic: string;\n}\nexport interface EnsureCommentRemovalConfigByContent {\n type: 'by-content';\n number: number;\n content: string;\n}\nexport type EnsureCommentRemovalConfig =\n | EnsureCommentRemovalConfigByTopic\n | EnsureCommentRemovalConfigByContent;\n\nexport type EnsureIssueResult = 'updated' | 'created';\n\nexport type RepoSortMethod =\n | 'alpha'\n | 'created'\n | 'updated'\n | 'size'\n | 'id'\n | null;\n\nexport type SortMethod = 'asc' | 'desc' | null;\nexport interface AutodiscoverConfig {\n topics?: string[];\n sort?: RepoSortMethod;\n order?: SortMethod;\n includeMirrors?: boolean;\n namespaces?: string[];\n projects?: string[];\n}\n\nexport interface Platform {\n findIssue(title: string): Promise<Issue | null>;\n getIssueList(): Promise<Issue[]>;\n getIssue?(number: number, memCache?: boolean): Promise<Issue | null>;\n getVulnerabilityAlerts?(): Promise<VulnerabilityAlert[]>;\n getRawFile(\n fileName: string,\n repoName?: string,\n branchOrTag?: string,\n ): Promise<string | null>;\n getJsonFile(\n fileName: string,\n repoName?: string,\n branchOrTag?: string,\n ): Promise<any>;\n initRepo(config: RepoParams): Promise<RepoResult>;\n getPrList(): Promise<Pr[]>;\n ensureIssueClosing(title: string): Promise<void>;\n ensureIssue(\n issueConfig: EnsureIssueConfig,\n ): Promise<EnsureIssueResult | null>;\n massageMarkdown(prBody: string): string;\n updatePr(prConfig: UpdatePrConfig): Promise<void>;\n mergePr(config: MergePRConfig): Promise<boolean>;\n addReviewers(number: number, reviewers: string[]): Promise<void>;\n addAssignees(number: number, assignees: string[]): Promise<void>;\n createPr(prConfig: CreatePRConfig): Promise<Pr | null>;\n getRepos(config?: AutodiscoverConfig): Promise<string[]>;\n getBranchForceRebase?(branchName: string): Promise<boolean>;\n deleteLabel(number: number, label: string): Promise<void>;\n addLabel?(number: number, label: string): Promise<void>;\n setBranchStatus(branchStatusConfig: BranchStatusConfig): Promise<void>;\n getBranchStatusCheck(\n branchName: string,\n // TODO: can be undefined or null ? #22198\n context: string | null | undefined,\n ): Promise<BranchStatus | null>;\n ensureCommentRemoval(\n ensureCommentRemoval:\n | EnsureCommentRemovalConfigByTopic\n | EnsureCommentRemovalConfigByContent,\n ): Promise<void>;\n ensureComment(ensureComment: EnsureCommentConfig): Promise<boolean>;\n getPr(number: number): Promise<Pr | null>;\n findPr(findPRConfig: FindPRConfig): Promise<Pr | null>;\n refreshPr?(number: number): Promise<void>;\n reattemptPlatformAutomerge?(\n prConfig: ReattemptPlatformAutomergeConfig,\n ): Promise<void>;\n getBranchStatus(\n branchName: string,\n internalChecksAsSuccess: boolean,\n ): Promise<BranchStatus>;\n getBranchPr(branchName: string, targetBranch?: string): Promise<Pr | null>;\n tryReuseAutoclosedPr?(pr: Pr): Promise<Pr | null>;\n initPlatform(config: PlatformParams): Promise<PlatformResult>;\n filterUnavailableUsers?(users: string[]): Promise<string[]>;\n commitFiles?(config: CommitFilesConfig): Promise<LongCommitSha | null>;\n expandGroupMembers?(reviewersOrAssignees: string[]): Promise<string[]>;\n\n maxBodyLength(): number;\n labelCharLimit?(): number;\n}\n\nexport interface PlatformScm {\n isBranchBehindBase(branchName: string, baseBranch: string): Promise<boolean>;\n isBranchModified(branchName: string, baseBranch: string): Promise<boolean>;\n isBranchConflicted(baseBranch: string, branch: string): Promise<boolean>;\n branchExists(branchName: string): Promise<boolean>;\n getBranchCommit(branchName: string): Promise<LongCommitSha | null>;\n deleteBranch(branchName: string): Promise<void>;\n commitAndPush(commitConfig: CommitFilesConfig): Promise<LongCommitSha | null>;\n getFileList(): Promise<string[]>;\n checkoutBranch(branchName: string): Promise<LongCommitSha>;\n mergeToLocal(branchName: string): Promise<void>;\n mergeAndPush(branchName: string): Promise<void>;\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../lib/modules/platform/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { MergeStrategy } from '../../config/types';\nimport type { BranchStatus, HostRule, VulnerabilityAlert } from '../../types';\nimport type { CommitFilesConfig, LongCommitSha } from '../../util/git/types';\n\ntype VulnerabilityKey = string;\ntype VulnerabilityRangeKey = string;\ntype VulnerabilityPatch = string;\nexport type AggregatedVulnerabilities = Record<\n VulnerabilityKey,\n Record<VulnerabilityRangeKey, VulnerabilityPatch | null>\n>;\n\nexport interface PlatformParams {\n endpoint?: string;\n token?: string;\n username?: string;\n password?: string;\n gitAuthor?: string;\n}\n\nexport interface PlatformResult {\n endpoint: string;\n renovateUsername?: string;\n token?: string;\n gitAuthor?: string;\n /*\n * return these only if _additional_ rules/hosts are required\n */\n hostRules?: HostRule[];\n}\n\nexport interface RepoResult {\n defaultBranch: string;\n isFork: boolean;\n repoFingerprint: string;\n}\n\nexport type GitUrlOption = 'default' | 'ssh' | 'endpoint';\n\nexport interface RepoParams {\n repository: string;\n endpoint?: string;\n gitUrl?: GitUrlOption;\n forkCreation?: boolean;\n forkOrg?: string;\n forkToken?: string;\n forkProcessing?: 'enabled' | 'disabled';\n renovateUsername?: string;\n cloneSubmodules?: boolean;\n cloneSubmodulesFilter?: string[];\n ignorePrAuthor?: boolean;\n bbUseDevelopmentBranch?: boolean;\n includeMirrors?: boolean;\n}\n\nexport interface PrDebugData {\n createdInVer: string;\n updatedInVer: string;\n targetBranch: string;\n labels?: string[];\n}\n\nexport interface PrBodyStruct {\n hash: string;\n rawConfigHash?: string;\n rebaseRequested?: boolean;\n debugData?: PrDebugData;\n}\n\n/**\n *\n */\nexport interface Pr {\n bodyStruct?: PrBodyStruct;\n sourceBranch: string;\n cannotMergeReason?: string; // for reflecting platform policies which may prevent merging\n createdAt?: string;\n closedAt?: string;\n hasAssignees?: boolean;\n labels?: string[];\n number: number;\n reviewers?: string[];\n sha?: LongCommitSha;\n sourceRepo?: string;\n state: string;\n targetBranch?: string;\n title: string;\n isDraft?: boolean;\n}\n\n/**\n * TODO: Proper typing\n */\nexport interface Issue {\n body?: string;\n number?: number;\n state?: string;\n title?: string;\n}\nexport interface PlatformPrOptions {\n autoApprove?: boolean;\n automergeStrategy?: MergeStrategy;\n azureWorkItemId?: number;\n bbUseDefaultReviewers?: boolean;\n bbAutoResolvePrTasks?: boolean;\n gitLabIgnoreApprovals?: boolean;\n usePlatformAutomerge?: boolean;\n forkModeDisallowMaintainerEdits?: boolean;\n}\n\nexport interface CreatePRConfig {\n sourceBranch: string;\n targetBranch: string;\n prTitle: string;\n prBody: string;\n labels?: string[] | null;\n platformPrOptions?: PlatformPrOptions;\n draftPR?: boolean;\n milestone?: number;\n}\nexport interface UpdatePrConfig {\n number: number;\n platformPrOptions?: PlatformPrOptions;\n prTitle: string;\n prBody?: string;\n state?: 'open' | 'closed';\n targetBranch?: string;\n\n /**\n * This field allows for label management and is designed to\n * accommodate the different label update methods on various platforms.\n *\n * - For Gitea, labels are updated by replacing the entire labels array.\n * - In the case of GitHub and GitLab, specific endpoints exist\n * for adding and removing labels.\n */\n labels?: string[] | null;\n\n /**\n * Specifies an array of labels to be added.\n * @see {@link labels}\n */\n addLabels?: string[] | null;\n\n /**\n * Specifies an array of labels to be removed.\n * @see {@link labels}\n */\n removeLabels?: string[] | null;\n}\nexport interface ReattemptPlatformAutomergeConfig {\n number: number;\n platformPrOptions?: PlatformPrOptions;\n}\nexport interface EnsureIssueConfig {\n title: string;\n reuseTitle?: string;\n body: string;\n labels?: string[];\n once?: boolean;\n shouldReOpen?: boolean;\n confidential?: boolean;\n}\nexport interface BranchStatusConfig {\n branchName: string;\n context: string;\n description: string;\n state: BranchStatus;\n url?: string;\n}\nexport interface FindPRConfig {\n branchName: string;\n prTitle?: string | null;\n state?: 'open' | 'closed' | '!open' | 'all';\n refreshCache?: boolean;\n targetBranch?: string | null;\n includeOtherAuthors?: boolean;\n}\nexport interface MergePRConfig {\n branchName?: string;\n id: number;\n strategy?: MergeStrategy;\n}\nexport interface EnsureCommentConfig {\n number: number;\n topic: string | null;\n content: string;\n}\n\nexport interface EnsureCommentRemovalConfigByTopic {\n type: 'by-topic';\n number: number;\n topic: string;\n}\nexport interface EnsureCommentRemovalConfigByContent {\n type: 'by-content';\n number: number;\n content: string;\n}\nexport type EnsureCommentRemovalConfig =\n | EnsureCommentRemovalConfigByTopic\n | EnsureCommentRemovalConfigByContent;\n\nexport type EnsureIssueResult = 'updated' | 'created';\n\nexport type RepoSortMethod =\n | 'alpha'\n | 'created'\n | 'updated'\n | 'size'\n | 'id'\n | null;\n\nexport type SortMethod = 'asc' | 'desc' | null;\nexport interface AutodiscoverConfig {\n topics?: string[];\n sort?: RepoSortMethod;\n order?: SortMethod;\n includeMirrors?: boolean;\n namespaces?: string[];\n projects?: string[];\n}\n\nexport interface Platform {\n findIssue(title: string): Promise<Issue | null>;\n getIssueList(): Promise<Issue[]>;\n getIssue?(number: number, memCache?: boolean): Promise<Issue | null>;\n getVulnerabilityAlerts?(): Promise<VulnerabilityAlert[]>;\n getRawFile(\n fileName: string,\n repoName?: string,\n branchOrTag?: string,\n ): Promise<string | null>;\n getJsonFile(\n fileName: string,\n repoName?: string,\n branchOrTag?: string,\n ): Promise<any>;\n initRepo(config: RepoParams): Promise<RepoResult>;\n getPrList(): Promise<Pr[]>;\n ensureIssueClosing(title: string): Promise<void>;\n ensureIssue(\n issueConfig: EnsureIssueConfig,\n ): Promise<EnsureIssueResult | null>;\n massageMarkdown(prBody: string): string;\n updatePr(prConfig: UpdatePrConfig): Promise<void>;\n mergePr(config: MergePRConfig): Promise<boolean>;\n addReviewers(number: number, reviewers: string[]): Promise<void>;\n addAssignees(number: number, assignees: string[]): Promise<void>;\n createPr(prConfig: CreatePRConfig): Promise<Pr | null>;\n getRepos(config?: AutodiscoverConfig): Promise<string[]>;\n getBranchForceRebase?(branchName: string): Promise<boolean>;\n deleteLabel(number: number, label: string): Promise<void>;\n addLabel?(number: number, label: string): Promise<void>;\n setBranchStatus(branchStatusConfig: BranchStatusConfig): Promise<void>;\n getBranchStatusCheck(\n branchName: string,\n // TODO: can be undefined or null ? #22198\n context: string | null | undefined,\n ): Promise<BranchStatus | null>;\n ensureCommentRemoval(\n ensureCommentRemoval:\n | EnsureCommentRemovalConfigByTopic\n | EnsureCommentRemovalConfigByContent,\n ): Promise<void>;\n ensureComment(ensureComment: EnsureCommentConfig): Promise<boolean>;\n getPr(number: number): Promise<Pr | null>;\n findPr(findPRConfig: FindPRConfig): Promise<Pr | null>;\n refreshPr?(number: number): Promise<void>;\n reattemptPlatformAutomerge?(\n prConfig: ReattemptPlatformAutomergeConfig,\n ): Promise<void>;\n getBranchStatus(\n branchName: string,\n internalChecksAsSuccess: boolean,\n ): Promise<BranchStatus>;\n getBranchPr(branchName: string, targetBranch?: string): Promise<Pr | null>;\n tryReuseAutoclosedPr?(pr: Pr): Promise<Pr | null>;\n initPlatform(config: PlatformParams): Promise<PlatformResult>;\n filterUnavailableUsers?(users: string[]): Promise<string[]>;\n commitFiles?(config: CommitFilesConfig): Promise<LongCommitSha | null>;\n expandGroupMembers?(reviewersOrAssignees: string[]): Promise<string[]>;\n\n maxBodyLength(): number;\n labelCharLimit?(): number;\n}\n\nexport interface PlatformScm {\n isBranchBehindBase(branchName: string, baseBranch: string): Promise<boolean>;\n isBranchModified(branchName: string, baseBranch: string): Promise<boolean>;\n isBranchConflicted(baseBranch: string, branch: string): Promise<boolean>;\n branchExists(branchName: string): Promise<boolean>;\n getBranchCommit(branchName: string): Promise<LongCommitSha | null>;\n deleteBranch(branchName: string): Promise<void>;\n commitAndPush(commitConfig: CommitFilesConfig): Promise<LongCommitSha | null>;\n getFileList(): Promise<string[]>;\n checkoutBranch(branchName: string): Promise<LongCommitSha>;\n mergeToLocal(branchName: string): Promise<void>;\n mergeAndPush(branchName: string): Promise<void>;\n syncForkWithUpstream?(baseBranch: string): Promise<void>;\n}\n"]}
@@ -21,6 +21,9 @@ export declare function branchExists(branchName: string): boolean;
21
21
  export declare function getBranchCommit(branchName: string): LongCommitSha | null;
22
22
  export declare function getCommitMessages(): Promise<string[]>;
23
23
  export declare function checkoutBranch(branchName: string): Promise<LongCommitSha>;
24
+ export declare function checkoutBranchFromRemote(branchName: string, remoteName: string): Promise<LongCommitSha>;
25
+ export declare function resetHardFromRemote(remoteAndBranch: string): Promise<void>;
26
+ export declare function forcePushToRemote(branchName: string, remote: string): Promise<void>;
24
27
  export declare function getFileList(): Promise<string[]>;
25
28
  export declare function getBranchList(): string[];
26
29
  export declare function isBranchBehindBase(branchName: string, baseBranch: string): Promise<boolean>;
@@ -113,3 +116,19 @@ export declare function clearRenovateRefs(): Promise<void>;
113
116
  *
114
117
  */
115
118
  export declare function listCommitTree(commitSha: LongCommitSha): Promise<TreeItem[]>;
119
+ /**
120
+ * Synchronize a forked branch with its upstream counterpart.
121
+ *
122
+ * syncForkWithUpstream updates the fork's branch, to match the corresponding branch in the upstream repository.
123
+ * The steps are:
124
+ * 1. Check if the branch exists locally.
125
+ * 2. If the branch exists locally: checkout the local branch.
126
+ * 3. If the branch does _not_ exist locally: checkout the upstream branch.
127
+ * 4. Reset the local branch to match the upstream branch.
128
+ * 5. Force push the (updated) local branch to the origin repository.
129
+ *
130
+ * @param {string} branchName - The name of the branch to synchronize.
131
+ * @returns {Promise<LongCommitSha>} - A promise that resolves to True if the synchronization is successful, or `false` if an error occurs.
132
+ */
133
+ export declare function syncForkWithUpstream(branchName: string): Promise<LongCommitSha>;
134
+ export declare function getRemotes(): Promise<string[]>;
@@ -18,6 +18,9 @@ exports.branchExists = branchExists;
18
18
  exports.getBranchCommit = getBranchCommit;
19
19
  exports.getCommitMessages = getCommitMessages;
20
20
  exports.checkoutBranch = checkoutBranch;
21
+ exports.checkoutBranchFromRemote = checkoutBranchFromRemote;
22
+ exports.resetHardFromRemote = resetHardFromRemote;
23
+ exports.forcePushToRemote = forcePushToRemote;
21
24
  exports.getFileList = getFileList;
22
25
  exports.getBranchList = getBranchList;
23
26
  exports.isBranchBehindBase = isBranchBehindBase;
@@ -39,6 +42,8 @@ exports.getUrl = getUrl;
39
42
  exports.pushCommitToRenovateRef = pushCommitToRenovateRef;
40
43
  exports.clearRenovateRefs = clearRenovateRefs;
41
44
  exports.listCommitTree = listCommitTree;
45
+ exports.syncForkWithUpstream = syncForkWithUpstream;
46
+ exports.getRemotes = getRemotes;
42
47
  const tslib_1 = require("tslib");
43
48
  const node_url_1 = tslib_1.__importDefault(require("node:url"));
44
49
  const promises_1 = require("timers/promises");
@@ -182,9 +187,10 @@ async function validateGitVersion() {
182
187
  logger_1.logger.debug(`Found valid git version: ${version}`);
183
188
  return true;
184
189
  }
185
- async function fetchBranchCommits() {
190
+ async function fetchBranchCommits(preferUpstream = true) {
186
191
  config.branchCommits = {};
187
- const opts = ['ls-remote', '--heads', config.url];
192
+ const url = preferUpstream && config.upstreamUrl ? config.upstreamUrl : config.url;
193
+ const opts = ['ls-remote', '--heads', url];
188
194
  if (config.extraCloneOpts) {
189
195
  Object.entries(config.extraCloneOpts).forEach((e) =>
190
196
  // TODO: types (#22198)
@@ -455,6 +461,21 @@ async function syncGit() {
455
461
  (await getDefaultBranch(git));
456
462
  /* v8 ignore next -- TODO: add test */
457
463
  delete (0, repository_1.getCache)()?.semanticCommits;
464
+ // If upstreamUrl is set then the bot is running in fork mode
465
+ // The "upstream" remote is the original repository which was forked from
466
+ if (config.upstreamUrl) {
467
+ logger_1.logger.debug(`Bringing default branch up-to-date with upstream, to get latest config`);
468
+ // Add remote if it does not exist
469
+ const remotes = await git.getRemotes(true);
470
+ if (!remotes.some((remote) => remote.name === 'upstream')) {
471
+ logger_1.logger.debug("Adding remote 'upstream'");
472
+ await git.addRemote('upstream', config.upstreamUrl);
473
+ }
474
+ await syncForkWithUpstream(config.currentBranch);
475
+ await fetchBranchCommits(false);
476
+ }
477
+ config.currentBranchSha = (await git.revparse('HEAD')).trim();
478
+ logger_1.logger.debug(`Current branch SHA: ${config.currentBranchSha}`);
458
479
  }
459
480
  async function getRepoStatus(path) {
460
481
  if (is_1.default.string(path)) {
@@ -503,7 +524,7 @@ async function checkoutBranch(branchName) {
503
524
  config.currentBranchSha = (await git.raw(['rev-parse', 'HEAD'])).trim();
504
525
  const latestCommitDate = (await git.log({ n: 1 }))?.latest?.date;
505
526
  if (latestCommitDate) {
506
- logger_1.logger.debug({ branchName, latestCommitDate }, 'latest commit');
527
+ logger_1.logger.debug({ branchName, latestCommitDate, sha: config.currentBranchSha }, 'latest commit');
507
528
  }
508
529
  await git.reset(simple_git_1.ResetMode.HARD);
509
530
  return config.currentBranchSha;
@@ -521,6 +542,50 @@ async function checkoutBranch(branchName) {
521
542
  throw err;
522
543
  }
523
544
  }
545
+ async function checkoutBranchFromRemote(branchName, remoteName) {
546
+ logger_1.logger.debug(`Checking out branch ${branchName} from remote ${remoteName}`);
547
+ await syncGit();
548
+ try {
549
+ await gitRetry(() => git.checkoutBranch(branchName, `${remoteName}/${branchName}`));
550
+ config.currentBranch = branchName;
551
+ config.currentBranchSha = (await git.revparse('HEAD')).trim();
552
+ logger_1.logger.debug(`Checked out branch ${branchName} from remote ${remoteName}`);
553
+ config.branchCommits[branchName] = config.currentBranchSha;
554
+ return config.currentBranchSha;
555
+ }
556
+ catch (err) {
557
+ const errChecked = (0, error_1.checkForPlatformFailure)(err);
558
+ /* v8 ignore next 3 -- hard to test */
559
+ if (errChecked) {
560
+ throw errChecked;
561
+ }
562
+ if (err.message?.includes('fatal: ambiguous argument')) {
563
+ logger_1.logger.warn({ err }, 'Failed to checkout branch');
564
+ throw new Error(error_messages_1.TEMPORARY_ERROR);
565
+ }
566
+ throw err;
567
+ }
568
+ }
569
+ async function resetHardFromRemote(remoteAndBranch) {
570
+ try {
571
+ const resetLog = await git.reset(['--hard', remoteAndBranch]);
572
+ logger_1.logger.debug({ resetLog }, 'git reset log');
573
+ }
574
+ catch (err) {
575
+ logger_1.logger.error({ err }, 'Error during git reset --hard');
576
+ throw err;
577
+ }
578
+ }
579
+ async function forcePushToRemote(branchName, remote) {
580
+ try {
581
+ const pushLog = await git.push([remote, branchName, '--force']);
582
+ logger_1.logger.debug({ pushLog }, 'git push log');
583
+ }
584
+ catch (err) {
585
+ logger_1.logger.error({ err }, 'Error during git push --force');
586
+ throw err;
587
+ }
588
+ }
524
589
  async function getFileList() {
525
590
  await syncGit();
526
591
  const branch = config.currentBranch;
@@ -1186,4 +1251,50 @@ async function listCommitTree(commitSha) {
1186
1251
  }
1187
1252
  return result;
1188
1253
  }
1254
+ async function localBranchExists(branchName) {
1255
+ await syncGit();
1256
+ const localBranches = await git.branchLocal();
1257
+ return localBranches.all.includes(branchName);
1258
+ }
1259
+ /**
1260
+ * Synchronize a forked branch with its upstream counterpart.
1261
+ *
1262
+ * syncForkWithUpstream updates the fork's branch, to match the corresponding branch in the upstream repository.
1263
+ * The steps are:
1264
+ * 1. Check if the branch exists locally.
1265
+ * 2. If the branch exists locally: checkout the local branch.
1266
+ * 3. If the branch does _not_ exist locally: checkout the upstream branch.
1267
+ * 4. Reset the local branch to match the upstream branch.
1268
+ * 5. Force push the (updated) local branch to the origin repository.
1269
+ *
1270
+ * @param {string} branchName - The name of the branch to synchronize.
1271
+ * @returns {Promise<LongCommitSha>} - A promise that resolves to True if the synchronization is successful, or `false` if an error occurs.
1272
+ */
1273
+ async function syncForkWithUpstream(branchName) {
1274
+ const remotes = await getRemotes();
1275
+ if (!remotes.some((r) => r === 'upstream')) {
1276
+ throw new Error('No remote named "upstream" exists, cannot sync fork');
1277
+ }
1278
+ try {
1279
+ await git.fetch(['upstream']);
1280
+ if (await localBranchExists(branchName)) {
1281
+ await checkoutBranch(branchName);
1282
+ }
1283
+ else {
1284
+ await checkoutBranchFromRemote(branchName, `upstream`);
1285
+ }
1286
+ await resetHardFromRemote(`upstream/${branchName}`);
1287
+ await forcePushToRemote(branchName, 'origin');
1288
+ // Get long Git SHA
1289
+ return (await git.revparse([branchName]));
1290
+ }
1291
+ catch (err) {
1292
+ logger_1.logger.error({ err }, 'Error synchronizing fork');
1293
+ throw new Error(error_messages_1.UNKNOWN_ERROR);
1294
+ }
1295
+ }
1296
+ async function getRemotes() {
1297
+ const remotes = await git.getRemotes();
1298
+ return remotes.map((remote) => remote.name);
1299
+ }
1189
1300
  //# sourceMappingURL=index.js.map