Skip to content
Open
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
22 changes: 16 additions & 6 deletions tools/pipelines/localization-update.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
steps:
- checkout: self
clean: true
fetchDepth: 1
fetchDepth: 0
fetchTags: false
submodules: false

Expand Down Expand Up @@ -207,27 +207,37 @@ jobs:
git config --local http.https://github.com/.extraheader "AUTHORIZATION: basic $basicAuth"
git remote set-url origin https://github.com/microsoft/TypeScript.git

$existingPr = gh pr list --repo microsoft/TypeScript --base main --head $branch --state open --json url --jq '.[0].url // empty'
$remoteBranch = git ls-remote --heads origin $branch
$branchExists = [bool] $remoteBranch
$remoteCommit = ""
if ($branchExists) {
git fetch --depth=1 --no-tags --progress origin "refs/heads/${branch}:refs/remotes/origin/${branch}"
git fetch --no-tags --progress origin "refs/heads/${branch}:refs/remotes/origin/${branch}"
$remoteCommit = git rev-parse "origin/${branch}"
}

$candidate = git rev-parse HEAD
if ($existingPr) {
if (!$branchExists) {
throw "The open localization pull request no longer has a remote branch."
}
$candidate = node tools/scripts/preserve-localization-base.mjs $remoteCommit
}

$shouldPush = $true
if ($branchExists) {
$localTree = git rev-parse 'HEAD^{tree}'
$remoteTree = git rev-parse "origin/${branch}^{tree}"
$localTree = git rev-parse "${candidate}^{tree}"
$remoteTree = git rev-parse "${remoteCommit}^{tree}"
if ($localTree -eq $remoteTree) {
Write-Host "Remote branch already contains the same files."
$shouldPush = $false
}
}

if ($shouldPush) {
git push --force-with-lease --set-upstream origin $branch
git push "--force-with-lease=refs/heads/${branch}:${remoteCommit}" origin "${candidate}:refs/heads/${branch}"
}

$existingPr = gh pr list --repo microsoft/TypeScript --base main --head $branch --state open --json url --jq '.[0].url // empty'
if ($existingPr) {
Write-Host "Updated existing pull request: $existingPr"
}
Expand Down
48 changes: 48 additions & 0 deletions tools/scripts/preserve-localization-base.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import {
execFileSync,
spawnSync,
} from "node:child_process";

const [previous] = process.argv.slice(2);
if (!previous || process.argv.length !== 3) {
throw new Error("Usage: node tools/scripts/preserve-localization-base.mjs <previous-localization-commit>");
}

console.log(prepareUpdate());

function prepareUpdate() {
const generated = git("rev-parse", "HEAD");
const main = git("rev-parse", `${generated}^`);
const previousHead = git("rev-parse", "--verify", `${previous}^{commit}`);
const base = git("merge-base", main, previousHead);
Comment on lines +13 to +17
if (base === main) return generated;

// Apply only the generated delta to the old base, without changing the checkout.
const tree = mergeTree(`--merge-base=${main}`, base, generated);
if (!tree) {
console.error("Localization changes conflict with the existing PR base; using current main.");
return generated;
}

const candidate = git("commit-tree", tree, "-p", base, "-m", "Update localization files");
if (mergeTree(main, candidate) !== git("rev-parse", `${generated}^{tree}`)) {
console.error("Preserving the PR base would conflict or change the generated merge result; using current main.");
return generated;
}

return tree === git("rev-parse", `${previousHead}^{tree}`) ? previousHead : candidate;
}

function git(...args) {
return execFileSync("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
}

function mergeTree(...args) {
const result = spawnSync("git", ["merge-tree", "--write-tree", ...args], { encoding: "utf8" });
if (result.error) throw result.error;
if (result.status === 1) return undefined;
if (result.status !== 0) {
throw new Error(`git merge-tree failed (${result.status}): ${result.stderr}`);
}
return result.stdout.trim().split(/\r?\n/, 1)[0];
}
Loading