Skip to content

Commit e9e95c8

Browse files
committed
feat(#181): reconstruct script in failure comment
1 parent baae3fe commit e9e95c8

File tree

5 files changed

+470
-118
lines changed

5 files changed

+470
-118
lines changed

dist/cli/index.js

Lines changed: 131 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1607,13 +1607,6 @@ class Runner {
16071607
});
16081608
}
16091609
catch (error) {
1610-
this.logger.error(`Something went wrong backporting to ${pr.base}: ${error}`);
1611-
if (!configs.dryRun && configs.errorNotification.enabled && configs.errorNotification.message.length > 0) {
1612-
// notify the failure as comment in the original pull request
1613-
let comment = (0, runner_util_1.injectError)(configs.errorNotification.message, error);
1614-
comment = (0, runner_util_1.injectTargetBranch)(comment, pr.base);
1615-
await gitApi.createPullRequestComment(configs.originalPullRequest.url, comment);
1616-
}
16171610
failures.push(error);
16181611
}
16191612
}
@@ -1641,41 +1634,143 @@ class Runner {
16411634
return token;
16421635
}
16431636
async executeBackport(configs, backportPR, git) {
1644-
this.logger.setContext(backportPR.base);
1645-
const originalPR = configs.originalPullRequest;
1646-
// 4. clone the repository
1647-
this.logger.debug("Cloning repo..");
1648-
await git.gitCli.clone(configs.originalPullRequest.targetRepo.cloneUrl, configs.folder, backportPR.base);
1649-
// 5. create new branch from target one and checkout
1650-
this.logger.debug("Creating local branch..");
1651-
await git.gitCli.createLocalBranch(configs.folder, backportPR.head);
1652-
// 6. fetch pull request remote if source owner != target owner or pull request still open
1653-
if (configs.originalPullRequest.sourceRepo.owner !== configs.originalPullRequest.targetRepo.owner ||
1654-
configs.originalPullRequest.state === "open") {
1655-
this.logger.debug("Fetching pull request remote..");
1656-
const prefix = git.gitClientType === git_types_1.GitClientType.GITLAB ? "merge-requests" : "pull"; // default is for gitlab
1657-
await git.gitCli.fetch(configs.folder, `${prefix}/${configs.originalPullRequest.number}/head:pr/${configs.originalPullRequest.number}`);
1637+
let i = 0;
1638+
for (const step of backportSteps(this.logger, configs, backportPR, git.gitCli, git.gitClientType, () => git.gitClientApi.createPullRequest(backportPR))) {
1639+
try {
1640+
await step();
1641+
}
1642+
catch (error) {
1643+
this.logger.error(`Something went wrong backporting to ${backportPR.base}: ${error}`);
1644+
if (!configs.dryRun && configs.errorNotification.enabled && configs.errorNotification.message.length > 0) {
1645+
// notify the failure as comment in the original pull request
1646+
let comment = (0, runner_util_1.injectError)(configs.errorNotification.message, error);
1647+
comment = (0, runner_util_1.injectTargetBranch)(comment, backportPR.base);
1648+
try {
1649+
let script = "Reconstruction of the attempted steps (beware that escaping may be missing):\n```sh\n";
1650+
script += await backportScript(configs, backportPR, git, i);
1651+
script += "```";
1652+
comment += "\n\n" + script;
1653+
}
1654+
catch (scriptError) {
1655+
this.logger.error(`Something went wrong reconstruction the script: ${scriptError}`);
1656+
}
1657+
await git.gitClientApi.createPullRequestComment(configs.originalPullRequest.url, comment);
1658+
}
1659+
throw error;
1660+
}
1661+
i++;
16581662
}
1659-
// 7. apply all changes to the new branch
1660-
this.logger.debug("Cherry picking commits..");
1661-
for (const sha of originalPR.commits) {
1662-
await git.gitCli.cherryPick(configs.folder, sha, configs.mergeStrategy, configs.mergeStrategyOption, configs.cherryPickOptions);
1663+
return;
1664+
}
1665+
}
1666+
exports["default"] = Runner;
1667+
async function backportScript(configs, backportPR, git, failed) {
1668+
let s = "";
1669+
const discardLogger = {
1670+
setContext: function (_newContext) { },
1671+
getContext: function () { return undefined; },
1672+
clearContext: function () { },
1673+
trace: function (_message) { },
1674+
debug: function (_message) { },
1675+
info: function (_message) { },
1676+
warn: function (_message) { },
1677+
error: function (_message) { }
1678+
};
1679+
const fakeGitCli = {
1680+
clone: async function (_from, _to, _branch) {
1681+
/* consider that the user already has the repo cloned (or knows how to clone) */
1682+
},
1683+
createLocalBranch: async function (_cwd, newBranch) {
1684+
s += `git fetch origin ${backportPR.base}\n`;
1685+
s += `git switch -c ${newBranch} origin/${backportPR.base}`;
1686+
},
1687+
fetch: async function (_cwd, branch, remote = "origin") {
1688+
s += `git fetch ${remote} ${branch}`;
1689+
},
1690+
cherryPick: async function (_cwd, sha, strategy = "recursive", strategyOption = "theirs", cherryPickOptions) {
1691+
s += `git cherry-pick -m 1 --strategy=${strategy} --strategy-option=${strategyOption} `;
1692+
if (cherryPickOptions !== undefined) {
1693+
s += cherryPickOptions + " ";
1694+
}
1695+
s += sha;
1696+
},
1697+
push: async function (_cwd, branch, remote = "origin", force = false) {
1698+
s += `git push ${remote} ${branch}`;
1699+
if (force) {
1700+
s += " --force";
1701+
}
16631702
}
1664-
if (!configs.dryRun) {
1665-
// 8. push the new branch to origin
1666-
await git.gitCli.push(configs.folder, backportPR.head);
1667-
// 9. create pull request new branch -> target branch (using octokit)
1668-
const prUrl = await git.gitClientApi.createPullRequest(backportPR);
1669-
this.logger.info(`Pull request created: ${prUrl}`);
1703+
};
1704+
let i = 0;
1705+
let gathered = "";
1706+
for (const step of backportSteps(discardLogger, configs, backportPR, fakeGitCli, git.gitClientType, async () => {
1707+
s += `# ${git.gitClientType}.createPullRequest`;
1708+
return "";
1709+
})) {
1710+
if (i == failed) {
1711+
s += "# the step below failed\n";
16701712
}
1671-
else {
1672-
this.logger.warn("Pull request creation and remote push skipped");
1673-
this.logger.info(`${JSON.stringify(backportPR, null, 2)}`);
1713+
await step();
1714+
if (s.length > 0) {
1715+
gathered += s + "\n";
1716+
s = "";
16741717
}
1675-
this.logger.clearContext();
1718+
i++;
16761719
}
1720+
return gathered;
1721+
}
1722+
function* backportSteps(logger, configs, backportPR, gitCli, gitClientType, createPullRequest) {
1723+
// every failible operation should be in a dedicated closure
1724+
// 4. clone the repository
1725+
yield async () => {
1726+
logger.setContext(backportPR.base);
1727+
logger.debug("Cloning repo..");
1728+
await gitCli.clone(configs.originalPullRequest.targetRepo.cloneUrl, configs.folder, backportPR.base);
1729+
};
1730+
// 5. create new branch from target one and checkout
1731+
yield async () => {
1732+
logger.debug("Creating local branch..");
1733+
await gitCli.createLocalBranch(configs.folder, backportPR.head);
1734+
};
1735+
// 6. fetch pull request remote if source owner != target owner or pull request still open
1736+
if (configs.originalPullRequest.sourceRepo.owner !== configs.originalPullRequest.targetRepo.owner ||
1737+
configs.originalPullRequest.state === "open") {
1738+
yield async () => {
1739+
logger.debug("Fetching pull request remote..");
1740+
const prefix = gitClientType === git_types_1.GitClientType.GITLAB ? "merge-requests" : "pull"; // default is for gitlab
1741+
await gitCli.fetch(configs.folder, `${prefix}/${configs.originalPullRequest.number}/head:pr/${configs.originalPullRequest.number}`);
1742+
};
1743+
}
1744+
// 7. apply all changes to the new branch
1745+
yield async () => {
1746+
logger.debug("Cherry picking commits..");
1747+
};
1748+
for (const sha of configs.originalPullRequest.commits) {
1749+
yield async () => {
1750+
await gitCli.cherryPick(configs.folder, sha, configs.mergeStrategy, configs.mergeStrategyOption, configs.cherryPickOptions);
1751+
};
1752+
}
1753+
if (!configs.dryRun) {
1754+
// 8. push the new branch to origin
1755+
yield async () => {
1756+
await gitCli.push(configs.folder, backportPR.head);
1757+
};
1758+
// 9. create pull request new branch -> target branch (using octokit)
1759+
yield async () => {
1760+
const prUrl = await createPullRequest();
1761+
logger.info(`Pull request created: ${prUrl}`);
1762+
};
1763+
}
1764+
else {
1765+
yield async () => {
1766+
logger.warn("Pull request creation and remote push skipped");
1767+
logger.info(`${JSON.stringify(backportPR, null, 2)}`);
1768+
};
1769+
}
1770+
yield async () => {
1771+
logger.clearContext();
1772+
};
16771773
}
1678-
exports["default"] = Runner;
16791774

16801775

16811776
/***/ }),

dist/gha/index.js

Lines changed: 131 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1572,13 +1572,6 @@ class Runner {
15721572
});
15731573
}
15741574
catch (error) {
1575-
this.logger.error(`Something went wrong backporting to ${pr.base}: ${error}`);
1576-
if (!configs.dryRun && configs.errorNotification.enabled && configs.errorNotification.message.length > 0) {
1577-
// notify the failure as comment in the original pull request
1578-
let comment = (0, runner_util_1.injectError)(configs.errorNotification.message, error);
1579-
comment = (0, runner_util_1.injectTargetBranch)(comment, pr.base);
1580-
await gitApi.createPullRequestComment(configs.originalPullRequest.url, comment);
1581-
}
15821575
failures.push(error);
15831576
}
15841577
}
@@ -1606,41 +1599,143 @@ class Runner {
16061599
return token;
16071600
}
16081601
async executeBackport(configs, backportPR, git) {
1609-
this.logger.setContext(backportPR.base);
1610-
const originalPR = configs.originalPullRequest;
1611-
// 4. clone the repository
1612-
this.logger.debug("Cloning repo..");
1613-
await git.gitCli.clone(configs.originalPullRequest.targetRepo.cloneUrl, configs.folder, backportPR.base);
1614-
// 5. create new branch from target one and checkout
1615-
this.logger.debug("Creating local branch..");
1616-
await git.gitCli.createLocalBranch(configs.folder, backportPR.head);
1617-
// 6. fetch pull request remote if source owner != target owner or pull request still open
1618-
if (configs.originalPullRequest.sourceRepo.owner !== configs.originalPullRequest.targetRepo.owner ||
1619-
configs.originalPullRequest.state === "open") {
1620-
this.logger.debug("Fetching pull request remote..");
1621-
const prefix = git.gitClientType === git_types_1.GitClientType.GITLAB ? "merge-requests" : "pull"; // default is for gitlab
1622-
await git.gitCli.fetch(configs.folder, `${prefix}/${configs.originalPullRequest.number}/head:pr/${configs.originalPullRequest.number}`);
1602+
let i = 0;
1603+
for (const step of backportSteps(this.logger, configs, backportPR, git.gitCli, git.gitClientType, () => git.gitClientApi.createPullRequest(backportPR))) {
1604+
try {
1605+
await step();
1606+
}
1607+
catch (error) {
1608+
this.logger.error(`Something went wrong backporting to ${backportPR.base}: ${error}`);
1609+
if (!configs.dryRun && configs.errorNotification.enabled && configs.errorNotification.message.length > 0) {
1610+
// notify the failure as comment in the original pull request
1611+
let comment = (0, runner_util_1.injectError)(configs.errorNotification.message, error);
1612+
comment = (0, runner_util_1.injectTargetBranch)(comment, backportPR.base);
1613+
try {
1614+
let script = "Reconstruction of the attempted steps (beware that escaping may be missing):\n```sh\n";
1615+
script += await backportScript(configs, backportPR, git, i);
1616+
script += "```";
1617+
comment += "\n\n" + script;
1618+
}
1619+
catch (scriptError) {
1620+
this.logger.error(`Something went wrong reconstruction the script: ${scriptError}`);
1621+
}
1622+
await git.gitClientApi.createPullRequestComment(configs.originalPullRequest.url, comment);
1623+
}
1624+
throw error;
1625+
}
1626+
i++;
16231627
}
1624-
// 7. apply all changes to the new branch
1625-
this.logger.debug("Cherry picking commits..");
1626-
for (const sha of originalPR.commits) {
1627-
await git.gitCli.cherryPick(configs.folder, sha, configs.mergeStrategy, configs.mergeStrategyOption, configs.cherryPickOptions);
1628+
return;
1629+
}
1630+
}
1631+
exports["default"] = Runner;
1632+
async function backportScript(configs, backportPR, git, failed) {
1633+
let s = "";
1634+
const discardLogger = {
1635+
setContext: function (_newContext) { },
1636+
getContext: function () { return undefined; },
1637+
clearContext: function () { },
1638+
trace: function (_message) { },
1639+
debug: function (_message) { },
1640+
info: function (_message) { },
1641+
warn: function (_message) { },
1642+
error: function (_message) { }
1643+
};
1644+
const fakeGitCli = {
1645+
clone: async function (_from, _to, _branch) {
1646+
/* consider that the user already has the repo cloned (or knows how to clone) */
1647+
},
1648+
createLocalBranch: async function (_cwd, newBranch) {
1649+
s += `git fetch origin ${backportPR.base}\n`;
1650+
s += `git switch -c ${newBranch} origin/${backportPR.base}`;
1651+
},
1652+
fetch: async function (_cwd, branch, remote = "origin") {
1653+
s += `git fetch ${remote} ${branch}`;
1654+
},
1655+
cherryPick: async function (_cwd, sha, strategy = "recursive", strategyOption = "theirs", cherryPickOptions) {
1656+
s += `git cherry-pick -m 1 --strategy=${strategy} --strategy-option=${strategyOption} `;
1657+
if (cherryPickOptions !== undefined) {
1658+
s += cherryPickOptions + " ";
1659+
}
1660+
s += sha;
1661+
},
1662+
push: async function (_cwd, branch, remote = "origin", force = false) {
1663+
s += `git push ${remote} ${branch}`;
1664+
if (force) {
1665+
s += " --force";
1666+
}
16281667
}
1629-
if (!configs.dryRun) {
1630-
// 8. push the new branch to origin
1631-
await git.gitCli.push(configs.folder, backportPR.head);
1632-
// 9. create pull request new branch -> target branch (using octokit)
1633-
const prUrl = await git.gitClientApi.createPullRequest(backportPR);
1634-
this.logger.info(`Pull request created: ${prUrl}`);
1668+
};
1669+
let i = 0;
1670+
let gathered = "";
1671+
for (const step of backportSteps(discardLogger, configs, backportPR, fakeGitCli, git.gitClientType, async () => {
1672+
s += `# ${git.gitClientType}.createPullRequest`;
1673+
return "";
1674+
})) {
1675+
if (i == failed) {
1676+
s += "# the step below failed\n";
16351677
}
1636-
else {
1637-
this.logger.warn("Pull request creation and remote push skipped");
1638-
this.logger.info(`${JSON.stringify(backportPR, null, 2)}`);
1678+
await step();
1679+
if (s.length > 0) {
1680+
gathered += s + "\n";
1681+
s = "";
16391682
}
1640-
this.logger.clearContext();
1683+
i++;
16411684
}
1685+
return gathered;
1686+
}
1687+
function* backportSteps(logger, configs, backportPR, gitCli, gitClientType, createPullRequest) {
1688+
// every failible operation should be in a dedicated closure
1689+
// 4. clone the repository
1690+
yield async () => {
1691+
logger.setContext(backportPR.base);
1692+
logger.debug("Cloning repo..");
1693+
await gitCli.clone(configs.originalPullRequest.targetRepo.cloneUrl, configs.folder, backportPR.base);
1694+
};
1695+
// 5. create new branch from target one and checkout
1696+
yield async () => {
1697+
logger.debug("Creating local branch..");
1698+
await gitCli.createLocalBranch(configs.folder, backportPR.head);
1699+
};
1700+
// 6. fetch pull request remote if source owner != target owner or pull request still open
1701+
if (configs.originalPullRequest.sourceRepo.owner !== configs.originalPullRequest.targetRepo.owner ||
1702+
configs.originalPullRequest.state === "open") {
1703+
yield async () => {
1704+
logger.debug("Fetching pull request remote..");
1705+
const prefix = gitClientType === git_types_1.GitClientType.GITLAB ? "merge-requests" : "pull"; // default is for gitlab
1706+
await gitCli.fetch(configs.folder, `${prefix}/${configs.originalPullRequest.number}/head:pr/${configs.originalPullRequest.number}`);
1707+
};
1708+
}
1709+
// 7. apply all changes to the new branch
1710+
yield async () => {
1711+
logger.debug("Cherry picking commits..");
1712+
};
1713+
for (const sha of configs.originalPullRequest.commits) {
1714+
yield async () => {
1715+
await gitCli.cherryPick(configs.folder, sha, configs.mergeStrategy, configs.mergeStrategyOption, configs.cherryPickOptions);
1716+
};
1717+
}
1718+
if (!configs.dryRun) {
1719+
// 8. push the new branch to origin
1720+
yield async () => {
1721+
await gitCli.push(configs.folder, backportPR.head);
1722+
};
1723+
// 9. create pull request new branch -> target branch (using octokit)
1724+
yield async () => {
1725+
const prUrl = await createPullRequest();
1726+
logger.info(`Pull request created: ${prUrl}`);
1727+
};
1728+
}
1729+
else {
1730+
yield async () => {
1731+
logger.warn("Pull request creation and remote push skipped");
1732+
logger.info(`${JSON.stringify(backportPR, null, 2)}`);
1733+
};
1734+
}
1735+
yield async () => {
1736+
logger.clearContext();
1737+
};
16421738
}
1643-
exports["default"] = Runner;
16441739

16451740

16461741
/***/ }),

0 commit comments

Comments
 (0)