Initial commit
This commit is contained in:
@@ -0,0 +1,123 @@
|
|||||||
|
(async () => {
|
||||||
|
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
|
||||||
|
const getAllDocuments = () => {
|
||||||
|
const docs = [document];
|
||||||
|
|
||||||
|
const scan = doc => {
|
||||||
|
[...doc.querySelectorAll("iframe, frame")].forEach(frame => {
|
||||||
|
try {
|
||||||
|
if (frame.contentDocument) {
|
||||||
|
docs.push(frame.contentDocument);
|
||||||
|
scan(frame.contentDocument);
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
scan(document);
|
||||||
|
return docs;
|
||||||
|
};
|
||||||
|
|
||||||
|
const waitForElement = async (selector, timeout = 30000) => {
|
||||||
|
const start = Date.now();
|
||||||
|
|
||||||
|
while (Date.now() - start < timeout) {
|
||||||
|
for (const doc of getAllDocuments()) {
|
||||||
|
const el = doc.querySelector(selector);
|
||||||
|
if (el) return el;
|
||||||
|
}
|
||||||
|
|
||||||
|
await sleep(300);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`No se encontró: ${selector}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const waitForEnabledDownload = async (timeout = 10 * 60 * 1000) => {
|
||||||
|
const start = Date.now();
|
||||||
|
|
||||||
|
while (Date.now() - start < timeout) {
|
||||||
|
for (const doc of getAllDocuments()) {
|
||||||
|
const button = doc.querySelector("#id_pub_button_download");
|
||||||
|
|
||||||
|
if (
|
||||||
|
button &&
|
||||||
|
button.offsetParent !== null &&
|
||||||
|
!button.classList.contains("disabled") &&
|
||||||
|
button.getAttribute("disabled") === null
|
||||||
|
) {
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await sleep(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error("El botón Download no se habilitó dentro del tiempo esperado");
|
||||||
|
};
|
||||||
|
|
||||||
|
const click = async el => {
|
||||||
|
el.scrollIntoView({ block: "center", inline: "center" });
|
||||||
|
el.focus();
|
||||||
|
await sleep(500);
|
||||||
|
|
||||||
|
const view = el.ownerDocument.defaultView || window;
|
||||||
|
["mouseover", "mousedown", "mouseup", "click"].forEach(type => {
|
||||||
|
el.dispatchEvent(
|
||||||
|
new view.MouseEvent(type, {
|
||||||
|
bubbles: true,
|
||||||
|
cancelable: true,
|
||||||
|
view
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
el.click();
|
||||||
|
};
|
||||||
|
|
||||||
|
const clickAndWait = async (selector, nextSelector) => {
|
||||||
|
const el = await waitForElement(selector);
|
||||||
|
await click(el);
|
||||||
|
await sleep(1200);
|
||||||
|
|
||||||
|
if (nextSelector) {
|
||||||
|
await waitForElement(nextSelector);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const deploy = await waitForElement("#id_menu_prj_deploy");
|
||||||
|
await click(deploy);
|
||||||
|
|
||||||
|
await sleep(1500);
|
||||||
|
|
||||||
|
await clickAndWait(
|
||||||
|
'input[name="avancar"][value="Next"][onclick="nm_next()"]',
|
||||||
|
'input[name="avancar"][value="Next"][onclick*="esquema"]'
|
||||||
|
);
|
||||||
|
|
||||||
|
await clickAndWait(
|
||||||
|
'input[name="avancar"][value="Next"][onclick*="esquema"]',
|
||||||
|
'input[name="avancar"][value="Next"][onclick*="perfil"]'
|
||||||
|
);
|
||||||
|
|
||||||
|
await clickAndWait(
|
||||||
|
'input[name="avancar"][value="Next"][onclick*="perfil"]',
|
||||||
|
'input[name="avancar"][value="Next"][onclick*="create_dir"]'
|
||||||
|
);
|
||||||
|
|
||||||
|
const lastNext = await waitForElement(
|
||||||
|
'input[name="avancar"][value="Next"][onclick*="create_dir"]'
|
||||||
|
);
|
||||||
|
|
||||||
|
await click(lastNext);
|
||||||
|
|
||||||
|
console.log("Esperando que se habilite Download...");
|
||||||
|
|
||||||
|
const downloadButton = await waitForEnabledDownload();
|
||||||
|
|
||||||
|
console.log("Download habilitado:", downloadButton);
|
||||||
|
await sleep(1500);
|
||||||
|
await click(await waitForEnabledDownload(30000));
|
||||||
|
console.log("Download clickeado.");
|
||||||
|
})();
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
(async () => {
|
||||||
|
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
|
||||||
|
const getAllDocuments = () => {
|
||||||
|
const docs = [document];
|
||||||
|
|
||||||
|
const scan = doc => {
|
||||||
|
[...doc.querySelectorAll("iframe, frame")].forEach(frame => {
|
||||||
|
try {
|
||||||
|
if (frame.contentDocument) {
|
||||||
|
docs.push(frame.contentDocument);
|
||||||
|
scan(frame.contentDocument);
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
scan(document);
|
||||||
|
return docs;
|
||||||
|
};
|
||||||
|
|
||||||
|
const waitForElement = async (selector, timeout = 20000) => {
|
||||||
|
const start = Date.now();
|
||||||
|
|
||||||
|
while (Date.now() - start < timeout) {
|
||||||
|
for (const doc of getAllDocuments()) {
|
||||||
|
const el = doc.querySelector(selector);
|
||||||
|
if (el) return el;
|
||||||
|
}
|
||||||
|
|
||||||
|
await sleep(300);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`No se encontró: ${selector}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const clickElement = async selector => {
|
||||||
|
const el = await waitForElement(selector);
|
||||||
|
el.scrollIntoView({ block: "center", inline: "center" });
|
||||||
|
await sleep(300);
|
||||||
|
el.click();
|
||||||
|
return el;
|
||||||
|
};
|
||||||
|
|
||||||
|
await clickElement("#id_menu_prj_generate");
|
||||||
|
|
||||||
|
await sleep(1500);
|
||||||
|
|
||||||
|
await clickElement('input[name="avancar"][value="Generate"]');
|
||||||
|
})();
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
(async () => {
|
||||||
|
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
|
||||||
|
const getAllDocuments = () => {
|
||||||
|
const docs = [document];
|
||||||
|
|
||||||
|
const scan = doc => {
|
||||||
|
[...doc.querySelectorAll("iframe, frame")].forEach(frame => {
|
||||||
|
try {
|
||||||
|
if (frame.contentDocument) {
|
||||||
|
docs.push(frame.contentDocument);
|
||||||
|
scan(frame.contentDocument);
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
scan(document);
|
||||||
|
return docs;
|
||||||
|
};
|
||||||
|
|
||||||
|
const waitForElement = async (selector, timeout = 15000) => {
|
||||||
|
const start = Date.now();
|
||||||
|
|
||||||
|
while (Date.now() - start < timeout) {
|
||||||
|
for (const doc of getAllDocuments()) {
|
||||||
|
const el = doc.querySelector(selector);
|
||||||
|
if (el) return el;
|
||||||
|
}
|
||||||
|
|
||||||
|
await sleep(300);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`No se encontró: ${selector}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const clickElement = async selector => {
|
||||||
|
const el = await waitForElement(selector);
|
||||||
|
el.scrollIntoView({ block: "center", inline: "center" });
|
||||||
|
await sleep(300);
|
||||||
|
el.click();
|
||||||
|
return el;
|
||||||
|
};
|
||||||
|
|
||||||
|
await clickElement("#id_menu_prj_version_increment");
|
||||||
|
|
||||||
|
await sleep(1000);
|
||||||
|
|
||||||
|
await clickElement('input[name="incrementar"][value="Increment"]');
|
||||||
|
})();
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
(async () => {
|
||||||
|
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
|
||||||
|
const getAllDocuments = () => {
|
||||||
|
const docs = [document];
|
||||||
|
|
||||||
|
const scan = doc => {
|
||||||
|
[...doc.querySelectorAll("iframe, frame")].forEach(frame => {
|
||||||
|
try {
|
||||||
|
if (frame.contentDocument) {
|
||||||
|
docs.push(frame.contentDocument);
|
||||||
|
scan(frame.contentDocument);
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
scan(document);
|
||||||
|
return docs;
|
||||||
|
};
|
||||||
|
|
||||||
|
const waitForElement = async (selector, timeout = 15000) => {
|
||||||
|
const start = Date.now();
|
||||||
|
|
||||||
|
while (Date.now() - start < timeout) {
|
||||||
|
for (const doc of getAllDocuments()) {
|
||||||
|
const el = doc.querySelector(selector);
|
||||||
|
if (el) return el;
|
||||||
|
}
|
||||||
|
|
||||||
|
await sleep(300);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`No se encontró: ${selector}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const clickElement = async selector => {
|
||||||
|
const el = await waitForElement(selector);
|
||||||
|
el.scrollIntoView({ block: "center", inline: "center" });
|
||||||
|
await sleep(300);
|
||||||
|
el.click();
|
||||||
|
return el;
|
||||||
|
};
|
||||||
|
|
||||||
|
await clickElement("#id_menu_prj_version_increment");
|
||||||
|
|
||||||
|
await sleep(1000);
|
||||||
|
|
||||||
|
await clickElement('input[name="incrementar"][value="Increment"]');
|
||||||
|
})();
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"manifest_version": 3,
|
||||||
|
"name": "Deploy Master",
|
||||||
|
"version": "1.2",
|
||||||
|
"description": "Ejecuta los pasos de deploy de Scriptcase desde archivos configurables.",
|
||||||
|
"permissions": ["activeTab", "scripting", "storage"],
|
||||||
|
"action": {
|
||||||
|
"default_popup": "popup.html",
|
||||||
|
"default_title": "Deploy Master"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,401 @@
|
|||||||
|
:root {
|
||||||
|
color-scheme: dark;
|
||||||
|
--bg: #111820;
|
||||||
|
--panel: #20303f;
|
||||||
|
--panel-2: #192633;
|
||||||
|
--line: #425569;
|
||||||
|
--line-soft: rgba(148, 163, 184, 0.24);
|
||||||
|
--text: #eef5ff;
|
||||||
|
--muted: #9aaabd;
|
||||||
|
--green: #36d787;
|
||||||
|
--blue: #3f9dff;
|
||||||
|
--blue-dark: #1b72d8;
|
||||||
|
--error: #ff6b6b;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
width: 322px;
|
||||||
|
margin: 0;
|
||||||
|
font-family: Inter, "Segoe UI", Arial, sans-serif;
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at 20% 0%, rgba(73, 130, 190, 0.28), transparent 28%),
|
||||||
|
linear-gradient(180deg, #182230 0%, #111820 100%);
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
input,
|
||||||
|
summary {
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shell {
|
||||||
|
min-height: 100vh;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.titlebar {
|
||||||
|
padding: 8px 12px 10px;
|
||||||
|
background: linear-gradient(180deg, #253244 0%, #172232 100%);
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.35);
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand h1 {
|
||||||
|
display: inline;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 21px;
|
||||||
|
line-height: 1;
|
||||||
|
font-weight: 750;
|
||||||
|
letter-spacing: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand span {
|
||||||
|
margin-left: 4px;
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rocket {
|
||||||
|
width: 27px;
|
||||||
|
height: 27px;
|
||||||
|
fill: #a9bdd2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
margin: 10px 6px;
|
||||||
|
padding: 12px 14px 10px;
|
||||||
|
border: 1px solid rgba(148, 163, 184, 0.24);
|
||||||
|
border-radius: 8px;
|
||||||
|
background:
|
||||||
|
linear-gradient(145deg, rgba(42, 60, 78, 0.92), rgba(25, 38, 51, 0.94)),
|
||||||
|
var(--panel);
|
||||||
|
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04), 0 6px 18px rgba(0, 0, 0, 0.22);
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
color: #f5f8fc;
|
||||||
|
font-size: 17px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title::after {
|
||||||
|
content: "";
|
||||||
|
flex: 1;
|
||||||
|
height: 1px;
|
||||||
|
background: linear-gradient(90deg, #7b8da1, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
font-size: 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch-row input,
|
||||||
|
.option input {
|
||||||
|
position: absolute;
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch {
|
||||||
|
position: relative;
|
||||||
|
width: 39px;
|
||||||
|
height: 21px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: #526171;
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.18);
|
||||||
|
transition: background 160ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 3px;
|
||||||
|
left: 4px;
|
||||||
|
width: 15px;
|
||||||
|
height: 15px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #f8fbff;
|
||||||
|
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);
|
||||||
|
transition: transform 160ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch-row input:checked + .switch,
|
||||||
|
.option input:checked + .mini-check {
|
||||||
|
background: linear-gradient(180deg, #65b8ff, #227cdb);
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch-row input:checked + .switch::after {
|
||||||
|
transform: translateX(16px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.steps {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: 12px;
|
||||||
|
margin: 4px 12px 16px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step {
|
||||||
|
display: grid;
|
||||||
|
justify-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
min-height: 78px;
|
||||||
|
color: var(--muted);
|
||||||
|
text-align: center;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.12;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 19px;
|
||||||
|
left: calc(50% + 22px);
|
||||||
|
width: calc(100% - 18px);
|
||||||
|
height: 3px;
|
||||||
|
background: var(--line);
|
||||||
|
border-radius: 999px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step:nth-child(2)::after {
|
||||||
|
top: 19px;
|
||||||
|
left: calc(50% + 22px);
|
||||||
|
width: calc(100% - 18px);
|
||||||
|
height: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step:nth-child(3)::after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step:nth-child(3)::before {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step.done::after,
|
||||||
|
.step.running::after,
|
||||||
|
.step.done::before,
|
||||||
|
.step.running::before {
|
||||||
|
background: var(--green);
|
||||||
|
box-shadow: 0 0 8px rgba(54, 215, 135, 0.28);
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-dot {
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
width: 38px;
|
||||||
|
height: 38px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #5b6470;
|
||||||
|
color: rgba(255, 255, 255, 0.65);
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-dot svg {
|
||||||
|
width: 23px;
|
||||||
|
height: 23px;
|
||||||
|
fill: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step.done,
|
||||||
|
.step.running {
|
||||||
|
color: #eefaf4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step.done .step-dot,
|
||||||
|
.step.running .step-dot {
|
||||||
|
background: linear-gradient(180deg, #46d98b, #23b86a);
|
||||||
|
color: #153927;
|
||||||
|
box-shadow: 0 0 14px rgba(54, 215, 135, 0.45);
|
||||||
|
}
|
||||||
|
|
||||||
|
.step.running .step-dot {
|
||||||
|
animation: pulse 900ms ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step.error .step-dot {
|
||||||
|
background: var(--error);
|
||||||
|
color: #391414;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step.skipped .step-dot {
|
||||||
|
background: #4b5563;
|
||||||
|
color: #a9b1bd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.primary-button {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 43px;
|
||||||
|
border: 1px solid rgba(180, 220, 255, 0.38);
|
||||||
|
border-radius: 7px;
|
||||||
|
color: white;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 650;
|
||||||
|
cursor: pointer;
|
||||||
|
background: linear-gradient(180deg, #5bb2ff 0%, #267de1 100%);
|
||||||
|
box-shadow: 0 0 12px rgba(63, 157, 255, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.26);
|
||||||
|
}
|
||||||
|
|
||||||
|
.primary-button:disabled,
|
||||||
|
.secondary-button:disabled {
|
||||||
|
opacity: 0.58;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-options {
|
||||||
|
margin-top: 10px;
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-options summary {
|
||||||
|
cursor: pointer;
|
||||||
|
list-style-position: inside;
|
||||||
|
}
|
||||||
|
|
||||||
|
.options-grid {
|
||||||
|
display: grid;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
color: #d7e2ed;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mini-check {
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
border-radius: 5px;
|
||||||
|
background: #55616e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mini-check::after {
|
||||||
|
content: "";
|
||||||
|
width: 8px;
|
||||||
|
height: 4px;
|
||||||
|
border-left: 2px solid white;
|
||||||
|
border-bottom: 2px solid white;
|
||||||
|
transform: rotate(-45deg) translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tools {
|
||||||
|
padding-bottom: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.secondary-button {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 9px;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 40px;
|
||||||
|
margin-top: 10px;
|
||||||
|
border: 1px solid rgba(148, 163, 184, 0.26);
|
||||||
|
border-radius: 7px;
|
||||||
|
color: #edf4fa;
|
||||||
|
background: rgba(30, 46, 61, 0.78);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.secondary-button svg {
|
||||||
|
width: 23px;
|
||||||
|
height: 23px;
|
||||||
|
fill: #d8e4ef;
|
||||||
|
}
|
||||||
|
|
||||||
|
.console {
|
||||||
|
margin: 8px 0 0;
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.07);
|
||||||
|
background: #1b1f22;
|
||||||
|
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.console-head {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
height: 28px;
|
||||||
|
padding: 0 8px 0 12px;
|
||||||
|
color: #d4d7da;
|
||||||
|
font-size: 13px;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.07);
|
||||||
|
}
|
||||||
|
|
||||||
|
.console-head::before {
|
||||||
|
content: "";
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-left: 4px solid transparent;
|
||||||
|
border-right: 4px solid transparent;
|
||||||
|
border-top: 5px solid #aab0b5;
|
||||||
|
margin-right: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.console-head span {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-button {
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
border: 0;
|
||||||
|
color: #b7bdc3;
|
||||||
|
background: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#logs {
|
||||||
|
min-height: 76px;
|
||||||
|
max-height: 130px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 8px 12px;
|
||||||
|
overflow: auto;
|
||||||
|
color: #d7d7d7;
|
||||||
|
font: 12px/1.22 Consolas, "Courier New", monospace;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-success {
|
||||||
|
color: #63dd8d;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse {
|
||||||
|
0%,
|
||||||
|
100% {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
transform: scale(1.08);
|
||||||
|
}
|
||||||
|
}
|
||||||
+73
@@ -0,0 +1,73 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="es">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Deploy Master</title>
|
||||||
|
<link rel="stylesheet" href="popup.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="shell">
|
||||||
|
<header class="titlebar">
|
||||||
|
<div class="brand">
|
||||||
|
<svg class="rocket" viewBox="0 0 24 24" aria-hidden="true">
|
||||||
|
<path d="M14.6 3.2c2.1-.9 4.4-1 6.2-.7.3 1.8.2 4.1-.7 6.2-1 2.4-2.9 4.6-5.9 6.7l-1.7-1.7-2.6 2.6-2.2-2.2 2.6-2.6-1.7-1.7c2.1-3 4.3-4.9 6.7-5.8ZM16 8.5a1.7 1.7 0 1 0 3.4 0A1.7 1.7 0 0 0 16 8.5ZM6.4 15.9l1.7 1.7c-.8 1.8-2.5 3.3-5.1 4 .7-2.6 2.2-4.3 4-5.1ZM4.6 9.7c1.2-2.3 2.7-3.9 4.8-4.8-.9 1.2-1.7 2.5-2.4 4L4.6 9.7Zm9.7 9.7.8-2.4c1.5-.7 2.8-1.5 4-2.4-.9 2.1-2.5 3.6-4.8 4.8Z"/>
|
||||||
|
</svg>
|
||||||
|
<div>
|
||||||
|
<h1>Deploy Master</h1>
|
||||||
|
<span>v1.2</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section class="panel">
|
||||||
|
<div class="section-title">
|
||||||
|
<span>Deploy Configuration</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label class="switch-row">
|
||||||
|
<input id="toggle-increment-version" type="checkbox" checked>
|
||||||
|
<span class="switch"></span>
|
||||||
|
<span>Incrementar y bloquear versión</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div id="steps" class="steps" aria-live="polite"></div>
|
||||||
|
|
||||||
|
<button id="run-deploy" class="primary-button" type="button">Ejecutar Deploy</button>
|
||||||
|
|
||||||
|
<details class="step-options">
|
||||||
|
<summary>Pasos a ejecutar</summary>
|
||||||
|
<div id="step-options" class="options-grid"></div>
|
||||||
|
</details>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="panel tools">
|
||||||
|
<div class="section-title">
|
||||||
|
<span>Herramientas de Desarrollador</span>
|
||||||
|
</div>
|
||||||
|
<button id="generate-test-id" class="secondary-button" type="button">
|
||||||
|
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||||
|
<path d="M4 5.5A2.5 2.5 0 0 1 6.5 3h11A2.5 2.5 0 0 1 20 5.5v13A2.5 2.5 0 0 1 17.5 21h-11A2.5 2.5 0 0 1 4 18.5v-13Zm2 0v13c0 .3.2.5.5.5h11c.3 0 .5-.2.5-.5v-13c0-.3-.2-.5-.5-.5h-11c-.3 0-.5.2-.5.5Zm2 2h5v2H8v-2Zm0 4h8v2H8v-2Zm0 4h8v2H8v-2Zm10-7h-3V7h3v1.5Zm0 3h-3v-1.5h3V11.5Z"/>
|
||||||
|
</svg>
|
||||||
|
Generar ID para prueba
|
||||||
|
</button>
|
||||||
|
<button id="download-reports-folder" class="secondary-button" type="button">
|
||||||
|
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||||
|
<path d="M12 3a5.8 5.8 0 0 1 5.6 4.4A5.3 5.3 0 0 1 17.2 18H15v-2h2.2a3.3 3.3 0 0 0 .1-6.6l-1.4-.1-.3-1.3A3.9 3.9 0 0 0 8 7.4l-.5 1.2-1.3.2A3.1 3.1 0 0 0 6.7 15H9v2H6.7A5.1 5.1 0 0 1 6 6.9 5.8 5.8 0 0 1 12 3Zm1 8v5.2l1.8-1.8 1.4 1.4L12 20l-4.2-4.2 1.4-1.4 1.8 1.8V11h2Z"/>
|
||||||
|
</svg>
|
||||||
|
Descargar carpeta de reportes
|
||||||
|
</button>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="console">
|
||||||
|
<div class="console-head">
|
||||||
|
<span>Consola de Logs</span>
|
||||||
|
<button id="clear-logs" class="icon-button" type="button" aria-label="Limpiar consola">x</button>
|
||||||
|
</div>
|
||||||
|
<pre id="logs"></pre>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<script src="popup.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,312 @@
|
|||||||
|
const DEPLOY_STEPS = [
|
||||||
|
{
|
||||||
|
id: "export-project",
|
||||||
|
label: "Exportar proyecto",
|
||||||
|
file: "export-project.js",
|
||||||
|
enabled: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "increment-version",
|
||||||
|
label: "Incrementar y bloquear version",
|
||||||
|
files: ["increment-version.js", "lock-version-history.js"],
|
||||||
|
enabled: true,
|
||||||
|
linkedToggle: "toggle-increment-version"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "generate-source-code",
|
||||||
|
label: "Generar codigo",
|
||||||
|
file: "generate-source-code.js",
|
||||||
|
enabled: true
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const REPORTS_DOWNLOAD_URL = "http://172.27.197.229:8092/scriptcase/app/GT_ARIES/blank_PROCESAR_DESCARGAR_REPORTES/";
|
||||||
|
const SCRIPT_DELAY_MS = 2500;
|
||||||
|
|
||||||
|
const CHECK_ICON = `
|
||||||
|
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||||
|
<path d="M9.2 16.6 4.8 12.2l-1.6 1.6 6 6L21 7.8l-1.6-1.6L9.2 16.6Z"/>
|
||||||
|
</svg>`;
|
||||||
|
|
||||||
|
const PLAY_ICON = `
|
||||||
|
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||||
|
<path d="M8 5v14l11-7L8 5Z"/>
|
||||||
|
</svg>`;
|
||||||
|
|
||||||
|
const SKIP_ICON = `
|
||||||
|
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||||
|
<path d="M5 7.4 6.4 6 12 11.6 17.6 6 19 7.4 13.4 13 19 18.6 17.6 20 12 14.4 6.4 20 5 18.6 10.6 13 5 7.4Z"/>
|
||||||
|
</svg>`;
|
||||||
|
|
||||||
|
const state = {
|
||||||
|
running: false,
|
||||||
|
stepStatus: Object.fromEntries(DEPLOY_STEPS.map(step => [step.id, "pending"])),
|
||||||
|
logs: []
|
||||||
|
};
|
||||||
|
|
||||||
|
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
|
||||||
|
const stepsEl = document.getElementById("steps");
|
||||||
|
const optionsEl = document.getElementById("step-options");
|
||||||
|
const logsEl = document.getElementById("logs");
|
||||||
|
const runButton = document.getElementById("run-deploy");
|
||||||
|
const generateIdButton = document.getElementById("generate-test-id");
|
||||||
|
const downloadReportsButton = document.getElementById("download-reports-folder");
|
||||||
|
const clearLogsButton = document.getElementById("clear-logs");
|
||||||
|
const incrementToggle = document.getElementById("toggle-increment-version");
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", init);
|
||||||
|
|
||||||
|
async function init() {
|
||||||
|
renderOptions();
|
||||||
|
renderSteps();
|
||||||
|
bindEvents();
|
||||||
|
await restoreOptions();
|
||||||
|
syncIncrementOption();
|
||||||
|
renderSteps();
|
||||||
|
log("INFO", "Deploy Master listo.");
|
||||||
|
}
|
||||||
|
|
||||||
|
function bindEvents() {
|
||||||
|
runButton.addEventListener("click", runDeploy);
|
||||||
|
generateIdButton.addEventListener("click", generateTestId);
|
||||||
|
downloadReportsButton.addEventListener("click", openReportsFolder);
|
||||||
|
clearLogsButton.addEventListener("click", () => {
|
||||||
|
state.logs = [];
|
||||||
|
renderLogs();
|
||||||
|
});
|
||||||
|
|
||||||
|
incrementToggle.addEventListener("change", () => {
|
||||||
|
syncIncrementOption();
|
||||||
|
persistOptions();
|
||||||
|
renderSteps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderOptions() {
|
||||||
|
optionsEl.innerHTML = DEPLOY_STEPS.map(step => `
|
||||||
|
<label class="option" title="${getStepFiles(step).join(", ")}">
|
||||||
|
<input type="checkbox" data-step-option="${step.id}" ${step.enabled ? "checked" : ""}>
|
||||||
|
<span class="mini-check"></span>
|
||||||
|
<span>${step.label}</span>
|
||||||
|
</label>
|
||||||
|
`).join("");
|
||||||
|
|
||||||
|
optionsEl.querySelectorAll("[data-step-option]").forEach(input => {
|
||||||
|
input.addEventListener("change", () => {
|
||||||
|
const step = DEPLOY_STEPS.find(item => item.id === input.dataset.stepOption);
|
||||||
|
step.enabled = input.checked;
|
||||||
|
|
||||||
|
if (step.linkedToggle === "toggle-increment-version") {
|
||||||
|
incrementToggle.checked = input.checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
persistOptions();
|
||||||
|
renderSteps();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderSteps() {
|
||||||
|
stepsEl.innerHTML = DEPLOY_STEPS.map(step => {
|
||||||
|
const status = step.enabled ? state.stepStatus[step.id] : "skipped";
|
||||||
|
return `
|
||||||
|
<div class="step ${status}">
|
||||||
|
<div class="step-dot">${getStepIcon(status)}</div>
|
||||||
|
<div>${step.label}</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}).join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStepIcon(status) {
|
||||||
|
if (status === "running") return PLAY_ICON;
|
||||||
|
if (status === "skipped") return SKIP_ICON;
|
||||||
|
return CHECK_ICON;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runDeploy() {
|
||||||
|
if (state.running) return;
|
||||||
|
|
||||||
|
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
|
||||||
|
if (!tab?.id) {
|
||||||
|
log("ERROR", "No se pudo detectar la pestaña activa.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectedSteps = DEPLOY_STEPS.filter(step => step.enabled);
|
||||||
|
if (!selectedSteps.length) {
|
||||||
|
log("WARN", "No hay pasos seleccionados para ejecutar.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setRunning(true);
|
||||||
|
resetStepStatus();
|
||||||
|
log("INFO", "Deploy iniciado...");
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (const step of DEPLOY_STEPS) {
|
||||||
|
if (!step.enabled) {
|
||||||
|
setStepStatus(step.id, "skipped");
|
||||||
|
log("INFO", `Saltando ${getStepFiles(step).join(", ")}.`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
setStepStatus(step.id, "running");
|
||||||
|
log("INFO", `Ejecutando ${getStepFiles(step).join(", ")}...`);
|
||||||
|
|
||||||
|
const stepFiles = getStepFiles(step);
|
||||||
|
|
||||||
|
for (const [index, file] of stepFiles.entries()) {
|
||||||
|
await chrome.scripting.executeScript({
|
||||||
|
target: { tabId: tab.id, allFrames: false },
|
||||||
|
files: [file]
|
||||||
|
});
|
||||||
|
|
||||||
|
if (index < stepFiles.length - 1) {
|
||||||
|
log("INFO", `Esperando ${SCRIPT_DELAY_MS / 1000}s antes del siguiente script...`);
|
||||||
|
await sleep(SCRIPT_DELAY_MS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setStepStatus(step.id, "done");
|
||||||
|
log("INFO", `${step.label} finalizado.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
log("SUCCESS", "Deploy completado exitosamente!");
|
||||||
|
} catch (error) {
|
||||||
|
const runningStep = DEPLOY_STEPS.find(step => state.stepStatus[step.id] === "running");
|
||||||
|
if (runningStep) {
|
||||||
|
setStepStatus(runningStep.id, "error");
|
||||||
|
}
|
||||||
|
|
||||||
|
log("ERROR", error?.message || "Ocurrio un error al ejecutar el deploy.");
|
||||||
|
} finally {
|
||||||
|
setRunning(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function openReportsFolder() {
|
||||||
|
await chrome.tabs.create({ url: REPORTS_DOWNLOAD_URL });
|
||||||
|
log("INFO", "Abriendo descarga de carpeta de reportes.");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function generateTestId() {
|
||||||
|
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
|
||||||
|
if (!tab?.id) {
|
||||||
|
log("ERROR", "No se pudo detectar la pestaña activa.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const [{ result }] = await chrome.scripting.executeScript({
|
||||||
|
target: { tabId: tab.id },
|
||||||
|
func: generateAndCopyTestId
|
||||||
|
});
|
||||||
|
|
||||||
|
log("INFO", `ID generado: ${result}`);
|
||||||
|
} catch (error) {
|
||||||
|
log("ERROR", error?.message || "No se pudo generar el ID de prueba.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateAndCopyTestId() {
|
||||||
|
const now = new Date();
|
||||||
|
const pad = value => String(value).padStart(2, "0");
|
||||||
|
const id = `TEST-${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
|
||||||
|
|
||||||
|
const textarea = document.createElement("textarea");
|
||||||
|
textarea.value = id;
|
||||||
|
textarea.style.position = "fixed";
|
||||||
|
textarea.style.top = "0";
|
||||||
|
textarea.style.left = "0";
|
||||||
|
textarea.style.opacity = "0";
|
||||||
|
document.body.appendChild(textarea);
|
||||||
|
textarea.select();
|
||||||
|
document.execCommand("copy");
|
||||||
|
document.body.removeChild(textarea);
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetStepStatus() {
|
||||||
|
DEPLOY_STEPS.forEach(step => {
|
||||||
|
state.stepStatus[step.id] = step.enabled ? "pending" : "skipped";
|
||||||
|
});
|
||||||
|
renderSteps();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setStepStatus(stepId, status) {
|
||||||
|
state.stepStatus[stepId] = status;
|
||||||
|
renderSteps();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setRunning(isRunning) {
|
||||||
|
state.running = isRunning;
|
||||||
|
runButton.disabled = isRunning;
|
||||||
|
generateIdButton.disabled = isRunning;
|
||||||
|
downloadReportsButton.disabled = isRunning;
|
||||||
|
runButton.textContent = isRunning ? "Ejecutando..." : "Ejecutar Deploy";
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncIncrementOption() {
|
||||||
|
const incrementStep = DEPLOY_STEPS.find(step => step.id === "increment-version");
|
||||||
|
incrementStep.enabled = incrementToggle.checked;
|
||||||
|
|
||||||
|
const option = optionsEl.querySelector('[data-step-option="increment-version"]');
|
||||||
|
if (option) {
|
||||||
|
option.checked = incrementToggle.checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function restoreOptions() {
|
||||||
|
const { deployStepOptions } = await chrome.storage.local.get("deployStepOptions");
|
||||||
|
if (!deployStepOptions) return;
|
||||||
|
|
||||||
|
DEPLOY_STEPS.forEach(step => {
|
||||||
|
if (typeof deployStepOptions[step.id] === "boolean") {
|
||||||
|
step.enabled = deployStepOptions[step.id];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
incrementToggle.checked = DEPLOY_STEPS.find(step => step.id === "increment-version").enabled;
|
||||||
|
|
||||||
|
optionsEl.querySelectorAll("[data-step-option]").forEach(input => {
|
||||||
|
const step = DEPLOY_STEPS.find(item => item.id === input.dataset.stepOption);
|
||||||
|
input.checked = step.enabled;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function persistOptions() {
|
||||||
|
const deployStepOptions = Object.fromEntries(DEPLOY_STEPS.map(step => [step.id, step.enabled]));
|
||||||
|
await chrome.storage.local.set({ deployStepOptions });
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStepFiles(step) {
|
||||||
|
return step.files || [step.file];
|
||||||
|
}
|
||||||
|
|
||||||
|
function log(level, message) {
|
||||||
|
const line = `[${level}] ${message}`;
|
||||||
|
state.logs.push({ level, line });
|
||||||
|
renderLogs();
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderLogs() {
|
||||||
|
logsEl.innerHTML = state.logs
|
||||||
|
.map(entry => {
|
||||||
|
const className = entry.level === "SUCCESS" ? "log-success" : "";
|
||||||
|
return `<span class="${className}">${escapeHtml(entry.line)}</span>`;
|
||||||
|
})
|
||||||
|
.join("\n");
|
||||||
|
logsEl.scrollTop = logsEl.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeHtml(value) {
|
||||||
|
return value
|
||||||
|
.replaceAll("&", "&")
|
||||||
|
.replaceAll("<", "<")
|
||||||
|
.replaceAll(">", ">")
|
||||||
|
.replaceAll('"', """)
|
||||||
|
.replaceAll("'", "'");
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user