Skip to content

Instantly share code, notes, and snippets.

@wevtimoteo
Last active August 8, 2023 17:01
Show Gist options
  • Save wevtimoteo/311dc62402bd61ab7ff81f680265ad28 to your computer and use it in GitHub Desktop.
Save wevtimoteo/311dc62402bd61ab7ff81f680265ad28 to your computer and use it in GitHub Desktop.
Retry GitHub app - Deliveries
const GitHubDeliveryManager = {
retryReasons: [
"Couldn't connect to server",
"Service Timeout",
"An Exception Occurred",
"502 Bad Gateway",
"timed out"
],
sleep: function(lf_ms) {
return new Promise(resolve => setTimeout(resolve, lf_ms));
},
submit: async function(form) {
var button = form.getElementsByClassName("btn");
if (button.length > 0) {
console.log("Redelivering...");
button[0].click();
}
await sleep(10000);
},
fetchForm: async function(delivery, wrapper) {
var form = delivery.getElementsByClassName("js-redeliver-hook-form");
if (form.length == 0) {
wrapper.click();
} else {
submit(form[0]);
}
},
getResult: function(delivery) {
return delivery.children[0].children[0].children[0].getAttribute("aria-label");
},
hasDeliverySucceeded: function(delivery) {
const result = this.getResult(delivery);
const success = delivery.getElementsByClassName("icon-for-success")[0];
if (typeof(success) == "undefined") {
return false;
}
const successDisplay = window.getComputedStyle(success).display;
return successDisplay != "none";
},
shouldRetry: function(delivery) {
const result = this.getResult(delivery);
return this.retryReasons.includes(result);
},
clearSucceeded: function() {
const deliveries = [].slice.call(document.getElementsByClassName("hook-delivery-item"));
Array.prototype.forEach.call(deliveries, function(delivery) {
if (GitHubDeliveryManager.hasDeliverySucceeded(delivery)) {
delivery.remove()
}
});
},
clearTooManyRequestsErrors: function() {
const fetchFormErrorMessages = [].slice.call(document.getElementsByClassName("is-error"));
Array.prototype.forEach.call(fetchFormErrorMessages, function(fetchFormErrorMessage) {
const deliveryItem = fetchFormErrorMessage.closest('.hook-delivery-item');
if (deliveryItem) {
deliveryItem.remove();
}
});
},
expandForms: function(quantity) {
const deliveries = [].slice.call(document.getElementsByClassName("hook-delivery-item")).slice(0, quantity);
Array.prototype.forEach.call(deliveries, function(delivery) {
if (GitHubDeliveryManager.hasDeliverySucceeded(delivery)) {
delivery.remove()
} else if (GitHubDeliveryManager.shouldRetry(delivery)) {
const wrapper = delivery.children[0].children[0].children;
if (wrapper.length > 0) {
GitHubDeliveryManager.fetchForm(delivery, wrapper[1]);
}
} else {
delivery.remove();
}
});
},
retry: function() {
const deliveryForms = document.getElementsByClassName("js-redeliver-hook-form");
Array.prototype.forEach.call(deliveryForms, function(deliveryForm) {
GitHubDeliveryManager.redeliverPayload(deliveryForm);
});
},
loadMore: function() {
document.querySelector(".js-hook-deliveries-pagination-button").click();
},
generateBoundary: function() {
const randomChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const randomPartLength = 16;
let boundary = '----WebKitFormBoundary';
for (let i = 0; i < randomPartLength; i++) {
boundary += randomChars.charAt(Math.floor(Math.random() * randomChars.length));
}
return boundary;
},
redeliverPayload: async function(form) {
try {
const actionUrl = form.getAttribute('action');
const authenticityToken = form.querySelector('input[name="authenticity_token"]').value;
const boundary = this.generateBoundary();
const payload = `--${boundary}\r\n` +
`Content-Disposition: form-data; name="authenticity_token"\r\n\r\n${authenticityToken}\r\n` +
`--${boundary}--`;
const response = await fetch(actionUrl, {
method: 'POST',
body: payload,
headers: {
'Accept': 'text/html',
'X-Requested-With': 'XMLHttpRequest',
'Content-Type': `multipart/form-data; boundary=${boundary}`
}
});
if (response.ok) {
const deliveryItem = form.closest('.hook-delivery-item');
if (deliveryItem) {
deliveryItem.remove();
}
} else {
console.error('Error redelivering payload.');
}
} catch (error) {
console.error('An error occurred:', error);
}
},
sleep: function(lf_ms) {
return new Promise(resolve => setTimeout(resolve, lf_ms));
},
startRetries: function(retryIntervalInSeconds) {
const retryInterval = setInterval(() => {
this.clearSucceeded();
this.clearTooManyRequestsErrors();
this.loadMore();
await this.sleep(2000);
for (let i = 0; i < 3; i++) {
this.expandForms(10);
this.retry();
await this.sleep(2000);
}
}, retryIntervalInSeconds * 1000);
return retryInterval;
},
stopRetries: function(interval) {
clearInterval(interval);
}
};
window.GitHubDeliveryManager = GitHubDeliveryManager;
const interval = GitHubDeliveryManager.startRetries(60);
// To stop the loop after a certain time
// GitHubDeliveryManager.stopRetries(interval);
@wevtimoteo
Copy link
Author

wevtimoteo commented Nov 13, 2019

If you do not want to keep hitting Load more deliveries button:

GitHubDeliveryManager.clearSucceeded();
GitHubDeliveryManager.loadMore();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment