vscode-makefile-tools/translations_auto_pr.js

283 строки
8.6 KiB
JavaScript

"use strict";
const fs = require("fs-extra");
const cp = require("child_process");
const Octokit = require("@octokit/rest");
const path = require("path");
const parseGitConfig = require("parse-git-config");
const branchName = "localization";
const mergeTo = "main";
const commitComment = "Localization - Translated Strings";
const pullRequestTitle = "[Auto] Localization - Translated Strings";
let repoOwner = process.argv[2];
let repoName = process.argv[3];
let authUser = process.argv[4];
let authToken = process.argv[5];
let userFullName = process.argv[6];
let userEmail = process.argv[7];
let locRootPath = process.argv[8];
let locSubPath = process.argv[9];
if (
!repoOwner ||
!repoName ||
!authUser ||
!authToken ||
!userFullName ||
!userEmail ||
!locRootPath ||
!locSubPath
) {
console.error(
`ERROR: Usage: ${path.parse(process.argv[0]).base} ${
path.parse(process.argv[1]).base
} repo_owner repo_name auth_token user_full_name user_email loc_root_path loc_sub_path`
);
console.error(
` repo_owner - The owner of the repo on GitHub. i.e. microsoft`
);
console.error(
` repo_name - The name of the repo on GitHub. i.e. vscode-makefile-tools`
);
console.error(
` auth_user - User account wiith permission to post a pull request against the GitHub repo.`
);
console.error(` auth_token - A PAT associated with auth_user.`);
console.error(
` user_full_name - A full name to associate with a git commit. (This is replaced by the PR account if commit is squashed.)`
);
console.error(
` user_email - An email to associate with a git commit. (This is replaced by the PR account if commit is squashed.)`
);
console.error(
` loc_root_path - The path to the folder with language-specific directories (containing localized xlf files).`
);
console.error(
` loc_sub_path - A sub-path after the language-specific directory, where the xlf to import is located. This should not include the name of the xlf file to import.)`
);
return;
}
console.log(`repoOwner=${repoOwner}`);
console.log(`repoName=${repoName}`);
console.log(`authUser=${authUser}`);
console.log(`userFullName=${userFullName}`);
console.log(`userEmail=${userEmail}`);
console.log(`locRootPath=${locRootPath}`);
console.log(`locSubPath=${locSubPath}`);
function hasBranch(branchName) {
console.log(
`Checking for existence of branch "${branchName}" (git branch --list ${branchName})`
);
let output = cp.execSync(`git branch --list ${branchName}`);
let lines = output.toString().split("\n");
let found = false;
lines.forEach((line) => {
found = found || line === ` ${branchName}`;
});
return found;
}
function hasAnyChanges() {
console.log("Checking if any files have changed (git status --porcelain)");
let output = cp.execSync("git status --porcelain");
let lines = output.toString().split("\n");
let anyChanges = false;
lines.forEach((line) => {
if (line != "") {
console.log("Change detected: " + line);
anyChanges = true;
}
});
return anyChanges;
}
// When invoked on build server, we should already be in a repo freshly synced to the mergeTo branch
if (hasAnyChanges()) {
console.log(
`Changes already present in this repo! This script is intended to be run against a freshly synced ${mergeTo} branch!`
);
return;
}
function sleep(ms) {
var unixtime_ms = new Date().getTime();
while (new Date().getTime() < unixtime_ms + ms) {}
}
console.log(
"This script is potentially DESTRUCTIVE! Cancel now, or it will proceed in 10 seconds."
);
sleep(10000);
let directories = [
"cs",
"de",
"es",
"fr",
"it",
"ja",
"ko",
"pl",
"pt-BR",
"ru",
"tr",
"zh-Hans",
"zh-Hant",
];
directories.forEach((languageId) => {
let sourcePath = `${locRootPath}\\${languageId}\\${locSubPath}\\${repoName}.${languageId}.xlf`;
let destinationPath = `./vscode-translations-import/${languageId}/vscode-extensions/${repoName}.xlf`;
console.log(`Copying "${sourcePath}" to "${destinationPath}"`);
fs.copySync(sourcePath, destinationPath);
});
console.log("Import translations into i18n directory");
cp.execSync("npm run translations-import");
if (!hasAnyChanges()) {
console.log("No changes detected");
return;
}
console.log("Changes detected");
console.log(`Ensure main ref is up to date locally (git fetch)`);
cp.execSync("git fetch");
// Remove old localization branch, if any
if (hasBranch("localization")) {
console.log(
`Remove old localization branch, if any (git branch -D localization)`
);
cp.execSync("git branch -D localization");
}
// Check out local branch
console.log(
`Creating local branch for changes (git checkout -b ${branchName})`
);
cp.execSync("git checkout -b localization");
// Add changed files.
console.log("Adding changed file (git add .)");
cp.execSync("git add .");
// git add may have resolves CR/LF's and there may not be anything to commit
if (!hasAnyChanges()) {
console.log(
"No changes detected. The only changes must have been due to CR/LF's, and have been corrected."
);
return;
}
// Set up user and permissions
// Save existing user name and email, in case already set.
var existingUserName;
var existingUserEmail;
var gitConfigPath = path.resolve(process.cwd(), "./.git/config");
var config = parseGitConfig.sync({ path: gitConfigPath });
if (typeof config === "object" && config.hasOwnProperty("user")) {
existingUserName = config.user.name;
existingUserEmail = config.user.email;
}
if (existingUserName === undefined) {
console.log(`Existing user name: undefined`);
} else {
console.log(`Existing user name: "${existingUserName}"`);
cp.execSync(`git config --local --unset user.name`);
}
if (existingUserEmail === undefined) {
console.log(`Existing user email: undefined`);
} else {
console.log(`Existing user email: "${existingUserEmail}"`);
cp.execSync(`git config --local --unset user.email`);
}
console.log(`Setting local user name to: "${userFullName}"`);
cp.execSync(`git config --local user.name "${userFullName}"`);
console.log(`Setting local user email to: "${userEmail}"`);
cp.execSync(`git config --local user.email "${userEmail}"`);
console.log(
`Configuring git with permission to push and to create pull requests (git remote remove origin && git remote add origin https://${authUser}:${authToken}@github.com/${repoOwner}/${repoName}.git`
);
cp.execSync("git remote remove origin");
cp.execSync(
`git remote add origin https://${authUser}:${authToken}@github.com/${repoOwner}/${repoName}.git`
);
// Commit changed files.
console.log(`Commiting changes (git commit -m "${commitComment}")`);
cp.execSync(`git commit -m "${commitComment}"`);
if (existingUserName === undefined) {
console.log(`Restoring original user name: undefined`);
cp.execSync(`git config --local --unset user.name`);
} else {
console.log(`Restoring original user name: "${existingUserName}"`);
cp.execSync(`git config --local user.name "${existingUserName}"`);
}
if (existingUserEmail === undefined) {
console.log(`Restoring original user email: undefined`);
cp.execSync(`git config --local --unset user.email`);
} else {
console.log(`Restoring original user email: "${existingUserEmail}"`);
cp.execSync(`git config --local user.email "${existingUserEmail}"`);
}
console.log(`pushing to remove branch (git push -f origin ${branchName})`);
cp.execSync(`git push -f origin ${branchName}`);
console.log("Checking if there is already a pull request...");
const octokit = new Octokit.Octokit({ auth: authToken });
octokit.pulls.list({ owner: repoOwner, repo: repoName }).then(({ data }) => {
let alreadyHasPullRequest = false;
if (data) {
data.forEach((pr) => {
alreadyHasPullRequest =
alreadyHasPullRequest || pr.title === pullRequestTitle;
});
}
// If not already present, create a PR against our remote branch.
if (!alreadyHasPullRequest) {
console.log("There is not already a pull request. Creating one.");
octokit.pulls.create({
body: "",
owner: repoOwner,
repo: repoName,
title: pullRequestTitle,
head: branchName,
base: mergeTo,
});
} else {
console.log("There is already a pull request.");
}
console.log(`Restoring default git permissions`);
cp.execSync("git remote remove origin");
cp.execSync(
`git remote add origin https://github.com/${repoOwner}/${repoName}.git`
);
console.log(`Run 'git fetch' against updated remote`);
cp.execSync("git fetch");
console.log(`Switching back to ${mergeTo} (git checkout ${mergeTo})`);
cp.execSync(`git checkout ${mergeTo}`);
console.log(`Remove localization branch (git branch -D localization)`);
cp.execSync("git branch -D localization");
});