mirror of
https://github.com/arcan1s/ahriman.git
synced 2025-04-26 08:17:17 +00:00
Add support of changes generation. Changes will be generated (unless explicitly asked not to) automatically during check process (i.e. `repo-update --dry-run` and aliases) and uploaded to the remote server. Changes can be reviewed either by web interface or by special subcommands. Changes will be automatically cleared during next successful build
313 lines
15 KiB
Django/Jinja
313 lines
15 KiB
Django/Jinja
<div id="package-info-modal" tabindex="-1" role="dialog" class="modal fade">
|
|
<div class="modal-dialog modal-xl" role="document">
|
|
<div class="modal-content">
|
|
<div id="package-info-modal-header" class="modal-header">
|
|
<h4 id="package-info" class="modal-title"></h4>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="form-group row mt-2">
|
|
<div class="col-4 col-lg-1" style="text-align: right">version</div>
|
|
<div id="package-info-version" class="col-8 col-lg-5"></div>
|
|
<div class="col-4 col-lg-1" style="text-align: right">packager</div>
|
|
<div id="package-info-packager" class="col-8 col-lg-5"></div>
|
|
</div>
|
|
|
|
<div class="form-group row mt-2">
|
|
<div class="col-4 col-lg-1" style="text-align: right">groups</div>
|
|
<div id="package-info-groups" class="col-8 col-lg-5"></div>
|
|
<div class="col-4 col-lg-1" style="text-align: right">licenses</div>
|
|
<div id="package-info-licenses" class="col-8 col-lg-5"></div>
|
|
</div>
|
|
|
|
<div class="form-group row mt-2">
|
|
<div class="col-4 col-lg-1" style="text-align: right">upstream</div>
|
|
<div id="package-info-upstream-url" class="col-8 col-lg-5"></div>
|
|
<div class="col-4 col-lg-1" style="text-align: right">AUR</div>
|
|
<div id="package-info-aur-url" class="col-8 col-lg-5"></div>
|
|
</div>
|
|
|
|
<div class="form-group row mt-2">
|
|
<div class="col-4 col-lg-1" style="text-align: right">packages</div>
|
|
<div id="package-info-packages" class="col-8 col-lg-5"></div>
|
|
<div class="col-4 col-lg-1" style="text-align: right">depends</div>
|
|
<div id="package-info-depends" class="col-8 col-lg-5"></div>
|
|
</div>
|
|
|
|
<hr class="col-12">
|
|
|
|
<div id="package-info-variables-block" hidden>
|
|
<h3>Environment variables</h3>
|
|
<div id="package-info-variables-div" class="form-group row"></div>
|
|
|
|
<hr class="col-12">
|
|
</div>
|
|
|
|
<nav>
|
|
<div class="nav nav-tabs" role="tablist">
|
|
<button id="package-info-logs-button" class="nav-link active" data-bs-toggle="tab" data-bs-target="#package-info-logs" type="button" role="tab" aria-controls="package-info-logs" aria-selected="true"><h3>Build logs</h3></button>
|
|
<button id="package-info-changes-button" class="nav-link" data-bs-toggle="tab" data-bs-target="#package-info-changes" type="button" role="tab" aria-controls="package-info-changes" aria-selected="false"><h3>Changes</h3></button>
|
|
</div>
|
|
</nav>
|
|
<div class="tab-content" id="nav-tabContent">
|
|
<div id="package-info-logs" class="tab-pane fade show active" role="tabpanel" aria-labelledby="package-info-logs-button" tabindex="0">
|
|
<pre class="language-console"><code id="package-info-logs-input" class="pre-scrollable language-console"></code><button id="package-info-logs-copy-button" type="button" class="btn language-console" onclick="copyLogs()"><i class="bi bi-clipboard"></i> copy</button></pre>
|
|
</div>
|
|
<div id="package-info-changes" class="tab-pane fade" role="tabpanel" aria-labelledby="package-info-changes-button" tabindex="0">
|
|
<pre class="language-diff"><code id="package-info-changes-input" class="pre-scrollable language-diff"></code><button id="package-info-changes-copy-button" type="button" class="btn language-diff" onclick="copyChanges()"><i class="bi bi-clipboard"></i> copy</button></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button id="package-info-update-button" type="submit" class="btn btn-success" onclick="packageInfoUpdate()" data-bs-dismiss="modal" hidden><i class="bi bi-play"></i><span class="d-none d-sm-inline"> update</span></button>
|
|
<button id="package-info-remove-button" type="submit" class="btn btn-danger" onclick="packageInfoRemove()" data-bs-dismiss="modal" hidden><i class="bi bi-trash"></i><span class="d-none d-sm-inline"> remove</span></button>
|
|
<button type="button" class="btn btn-secondary" onclick="showPackageInfo()"><i class="bi bi-arrow-clockwise"></i><span class="d-none d-sm-inline"> reload</span></button>
|
|
<button type="button" class="btn btn-primary" data-bs-dismiss="modal"><i class="bi bi-x"></i><span class="d-none d-sm-inline"> close</span></button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
const packageInfoModal = $("#package-info-modal");
|
|
const packageInfoModalHeader = $("#package-info-modal-header");
|
|
const packageInfo = $("#package-info");
|
|
packageInfoModal.on("hidden.bs.modal", () => {
|
|
packageInfoAurUrl.empty();
|
|
packageInfoDepends.empty();
|
|
packageInfoGroups.empty();
|
|
packageInfoLicenses.empty();
|
|
packageInfoPackager.empty();
|
|
packageInfoPackages.empty();
|
|
packageInfoUpstreamUrl.empty();
|
|
packageInfoVersion.empty();
|
|
|
|
packageInfoVariablesBlock.attr("hidden", true);
|
|
packageInfoVariablesDiv.empty();
|
|
|
|
packageInfoLogsInput.empty();
|
|
packageInfoChangesInput.empty();
|
|
|
|
packageInfoModal.trigger("reset");
|
|
|
|
hideInfoControls(true);
|
|
});
|
|
|
|
const packageInfoLogsInput = $("#package-info-logs-input");
|
|
const packageInfoLogsCopyButton = $("#package-info-logs-copy-button");
|
|
|
|
const packageInfoChangesInput = $("#package-info-changes-input");
|
|
const packageInfoChangesCopyButton = $("#package-info-changes-copy-button");
|
|
|
|
const packageInfoAurUrl = $("#package-info-aur-url");
|
|
const packageInfoDepends = $("#package-info-depends");
|
|
const packageInfoGroups = $("#package-info-groups");
|
|
const packageInfoLicenses = $("#package-info-licenses");
|
|
const packageInfoPackager = $("#package-info-packager");
|
|
const packageInfoPackages = $("#package-info-packages");
|
|
const packageInfoUpstreamUrl = $("#package-info-upstream-url");
|
|
const packageInfoVersion = $("#package-info-version");
|
|
|
|
const packageInfoVariablesBlock = $("#package-info-variables-block");
|
|
const packageInfoVariablesDiv = $("#package-info-variables-div");
|
|
|
|
async function copyChanges() {
|
|
const changes = packageInfoChangesInput.text();
|
|
await copyToClipboard(changes, packageInfoChangesCopyButton);
|
|
}
|
|
|
|
async function copyLogs() {
|
|
const logs = packageInfoLogsInput.text();
|
|
await copyToClipboard(logs, packageInfoLogsCopyButton);
|
|
}
|
|
|
|
function hideInfoControls(hidden) {
|
|
packageInfoRemoveButton.attr("hidden", hidden);
|
|
packageInfoUpdateButton.attr("hidden", hidden);
|
|
}
|
|
|
|
function insertVariable(packageBase, variable) {
|
|
const variableInput = document.createElement("div");
|
|
variableInput.classList.add("input-group");
|
|
|
|
const variableNameInput = document.createElement("input");
|
|
variableNameInput.classList.add("form-control");
|
|
variableNameInput.readOnly = true;
|
|
variableNameInput.value = variable.key;
|
|
|
|
const variableSeparator = document.createElement("span");
|
|
variableSeparator.classList.add("input-group-text")
|
|
variableSeparator.textContent = "=";
|
|
|
|
const variableValueInput = document.createElement("input");
|
|
variableValueInput.classList.add("form-control");
|
|
variableValueInput.readOnly = true;
|
|
variableValueInput.value = variable.value;
|
|
|
|
const variableButtonRemove = document.createElement("button");
|
|
variableButtonRemove.type = "button";
|
|
variableButtonRemove.classList.add("btn");
|
|
variableButtonRemove.classList.add("btn-outline-danger");
|
|
variableButtonRemove.innerHTML = "<i class=\"bi bi-trash\"></i>";
|
|
variableButtonRemove.onclick = _ => {
|
|
$.ajax({
|
|
url: `/api/v1/packages/${packageBase}/patches/${variable.key}`,
|
|
type: "DELETE",
|
|
dataType: "json",
|
|
success: _ => variableInput.remove(),
|
|
});
|
|
};
|
|
|
|
// bring them together
|
|
variableInput.appendChild(variableNameInput);
|
|
variableInput.appendChild(variableSeparator);
|
|
variableInput.appendChild(variableValueInput);
|
|
variableInput.appendChild(variableButtonRemove);
|
|
|
|
packageInfoVariablesDiv.append(variableInput);
|
|
}
|
|
|
|
function loadChanges(packageBase, onFailure) {
|
|
$.ajax({
|
|
url: `/api/v1/packages/${packageBase}/changes`,
|
|
data: {
|
|
architecture: repository.architecture,
|
|
repository: repository.repository,
|
|
},
|
|
type: "GET",
|
|
dataType: "json",
|
|
success: response => {
|
|
const changes = response.changes;
|
|
packageInfoChangesInput.text(changes || "");
|
|
packageInfoChangesInput.map((_, el) => hljs.highlightElement(el));
|
|
},
|
|
error: onFailure,
|
|
});
|
|
}
|
|
|
|
function loadLogs(packageBase, onFailure) {
|
|
$.ajax({
|
|
url: `/api/v2/packages/${packageBase}/logs`,
|
|
data: {
|
|
architecture: repository.architecture,
|
|
repository: repository.repository,
|
|
},
|
|
type: "GET",
|
|
dataType: "json",
|
|
success: response => {
|
|
const logs = response.map(log_record => {
|
|
return `[${new Date(1000 * log_record.created).toISOString()}] ${log_record.message}`;
|
|
});
|
|
packageInfoLogsInput.text(logs.join("\n"));
|
|
packageInfoLogsInput.map((_, el) => hljs.highlightElement(el));
|
|
},
|
|
error: onFailure,
|
|
});
|
|
}
|
|
|
|
function loadPackage(packageBase, onFailure) {
|
|
const headerClass = status => {
|
|
if (status === "pending") return ["bg-warning"];
|
|
if (status === "building") return ["bg-warning"];
|
|
if (status === "failed") return ["bg-danger", "text-white"];
|
|
if (status === "success") return ["bg-success", "text-white"];
|
|
return ["bg-secondary", "text-white"];
|
|
};
|
|
|
|
$.ajax({
|
|
url: `/api/v1/packages/${packageBase}`,
|
|
data: {
|
|
architecture: repository.architecture,
|
|
repository: repository.repository,
|
|
},
|
|
type: "GET",
|
|
dataType: "json",
|
|
success: response => {
|
|
const description = response.find(Boolean);
|
|
const packages = Object.keys(description.package.packages);
|
|
const aurUrl = description.package.remote.web_url;
|
|
const upstreamUrls = Array.from(
|
|
new Set(
|
|
Object.values(description.package.packages)
|
|
.map(single => single.url)
|
|
)
|
|
).sort();
|
|
|
|
packageInfo.text(`${description.package.base} ${description.status.status} at ${new Date(1000 * description.status.timestamp).toISOStringShort()}`);
|
|
|
|
packageInfoModalHeader.removeClass();
|
|
packageInfoModalHeader.addClass("modal-header");
|
|
headerClass(description.status.status).forEach(clz => packageInfoModalHeader.addClass(clz));
|
|
|
|
packageInfoAurUrl.html(aurUrl ? safeLink(aurUrl, aurUrl, "AUR link").outerHTML : "");
|
|
packageInfoDepends.html(listToTable(
|
|
Object.values(description.package.packages)
|
|
.reduce((accumulator, currentValue) => {
|
|
return accumulator.concat(currentValue.depends.filter(v => packages.indexOf(v) === -1))
|
|
.concat(currentValue.make_depends.filter(v => packages.indexOf(v) === -1).map(v => `${v} (make)`))
|
|
.concat(currentValue.opt_depends.filter(v => packages.indexOf(v) === -1).map(v => `${v} (optional)`));
|
|
}, [])
|
|
));
|
|
packageInfoGroups.html(listToTable(extractListProperties(description.package, "groups")));
|
|
packageInfoLicenses.html(listToTable(extractListProperties(description.package, "licenses")));
|
|
packageInfoPackager.text(description.package.packager);
|
|
packageInfoPackages.html(listToTable(packages));
|
|
packageInfoUpstreamUrl.html(upstreamUrls.map(url => safeLink(url, url, "upstream link").outerHTML).join("<br>"));
|
|
packageInfoVersion.text(description.package.version);
|
|
|
|
hideInfoControls(false);
|
|
},
|
|
error: (jqXHR, _, errorThrown) => {
|
|
hideInfoControls(true);
|
|
onFailure(jqXHR, null, errorThrown);
|
|
},
|
|
});
|
|
}
|
|
|
|
function loadPatches(packageBase, onFailure) {
|
|
$.ajax({
|
|
url: `/api/v1/packages/${packageBase}/patches`,
|
|
type: "GET",
|
|
dataType: "json",
|
|
success: response => {
|
|
packageInfoVariablesDiv.empty();
|
|
response.map(patch => insertVariable(packageBase, patch));
|
|
packageInfoVariablesBlock.attr("hidden", response.length === 0);
|
|
},
|
|
error: onFailure,
|
|
});
|
|
}
|
|
|
|
function packageInfoRemove() {
|
|
const packageBase = packageInfoModal.data("package");
|
|
if (packageBase) return packagesRemove([packageBase]);
|
|
}
|
|
|
|
function packageInfoUpdate() {
|
|
const packageBase = packageInfoModal.data("package");
|
|
if (packageBase) return packagesAdd(packageBase, [], repository);
|
|
}
|
|
|
|
function showPackageInfo(packageBase) {
|
|
const isPackageBaseSet = packageBase !== undefined;
|
|
if (isPackageBaseSet)
|
|
packageInfoModal.data("package", packageBase); // set package base as currently used
|
|
else
|
|
packageBase = packageInfoModal.data("package"); // read package base from the current window attribute
|
|
|
|
const onFailure = (jqXHR, _, errorThrown) => {
|
|
if (isPackageBaseSet) {
|
|
const message = error => `Could not load package ${packageBase} info: ${error}`;
|
|
showFailure("Load failure", message, jqXHR, errorThrown);
|
|
}
|
|
};
|
|
|
|
loadPackage(packageBase, onFailure);
|
|
loadPatches(packageBase, onFailure);
|
|
loadLogs(packageBase, onFailure);
|
|
loadChanges(packageBase, onFailure)
|
|
|
|
if (isPackageBaseSet) packageInfoModal.modal("show");
|
|
}
|
|
</script>
|