Using CodeScript ToolKit, you can make it so Obsidian updates all your themes and plugins every time it starts.
Save this as a .js file and point CodeScript Toolkit to it as your “Startup script path.”
exports.invoke = async (app) => {
async function clickButtonLabel(toclick) {
for (const el of document.querySelectorAll('.mod-cta')) {
const label = (el.textContent || '').trim();
if (label.startsWith(toclick)) {
el.click();
return true;
}
}
return false;
}
async function waitUntilGone(selector, { interval = 100, timeout = 15000 } = {}) {
const start = Date.now();
while (!document.querySelector(selector)) {
if (Date.now() - start > timeout) console.error(`Timeout waiting for ${selector} to appear`);
await new Promise(r => setTimeout(r, interval));
}
while (document.querySelector(selector)) {
if (Date.now() - start > timeout) console.error(`Timeout waiting for ${selector} to disappear`);
await new Promise(r => setTimeout(r, interval));
}
}
async function checkUpdates(tabId) {
app.setting.openTabById(tabId);
await clickButtonLabel("Check for updates");
await waitUntilGone('.vertical-tab-content.is-loading');
await clickButtonLabel("Update all");
}
app.setting.open();
await checkUpdates("community-plugins");
await checkUpdates("appearance");
app.setting.close();
};
Basically it:
- Opens the settings to the “Community plugins” tab,
- Clicks “Check for updates”
- Waits for the loading bar to go away
- Clicks the “Update all” button if it exists
- Does steps 1-4 for the “Appearance” tab to update themes
- Closes the settings
1 Like
Update for Obsidian 1.11.2
I know that, as of 1.11.0, Obsidian natively has an option to automatically check for plugin updates, but I made this to automatically install updates immediately or whenever I want without any manual input.
In my use case, I have an Obsidian “dashboard” page, that, anytime I start up Obsidian and the current date is diffent from its “LastLookedAt” property, it updates the property and checks for + installs updates. Effectively making it so that, the first time I open Obsidian for the day, all my plugins that have updates are updated.
As of Obsidian 1.11.2, the element .vertical-tab-content.is-loading, which was a loading bar that showed up after you tapped “Check for updates,” no longer shows. So, here’s a version of the startup script that updates all your plugins which doesn’t rely on that. Instead, it watches for the notice that pops up when the update check finishes. It also waits for tho notice to go away, which makes it slower than the original version (for some reason, making it click the notice to dismiss it breaks the entire settings view, so it just waits).
exports.invoke = async (app) => {
async function clickButtonLabel(toclick) {
for (const el of document.querySelectorAll('.mod-cta')) {
const label = (el.textContent || '').trim();
if (label.startsWith(toclick)) {
el.click();
return true;
}
}
return false;
}
async function waitForNoticeDismissal(toMatch) {
return new Promise((resolve) => {
const start = Date.now();
const timeout = 30000;
let foundNoticeElement = null;
const appearanceInterval = setInterval(() => {
if (Date.now() - start > timeout) {
clearInterval(appearanceInterval);
return resolve(false);
}
const notices = document.querySelectorAll('.notice');
for (let i = 0; i < notices.length; i++) {
const el = notices[i];
if (toMatch.test(el.textContent)) {
foundNoticeElement = el;
clearInterval(appearanceInterval);
const dismissalInterval = setInterval(() => {
if (!document.body.contains(foundNoticeElement)) {
clearInterval(dismissalInterval);
return resolve(true);
}
}, 200);
return;
}
}
}, 100);
});
}
async function checkUpdates(tabId) {
app.setting.openTabById(tabId);
await clickButtonLabel("Check for updates");
await waitForNoticeDismissal(/(Checking for updates\.\.\.|No \S+ updates found\.|Found [0-9]+ \S+ to update\.)/i);
await clickButtonLabel("Update all");
}
app.setting.open();
await checkUpdates("community-plugins");
await checkUpdates("appearance");
app.setting.close();
};
Sped up version that works on 1.11.2
Oh, on 1.11.2, executing a click on a notice doesn’t break anything. Though, I realized I can just .detach() the notice element, anyway. So, this version is as fast as the first version (maybe faster) and works on 1.11.2.
exports.invoke = async (app) => {
async function clickButtonLabel(toclick) {
for (const el of document.querySelectorAll('.mod-cta')) {
const label = (el.textContent || '').trim();
if (label.startsWith(toclick)) {
el.click();
return true;
}
}
return false;
}
async function waitForNotice(toMatch) {
return new Promise((resolve) => {
const start = Date.now();
const timeout = 30000;
let foundNoticeElement = null;
const appearanceInterval = setInterval(() => {
if (Date.now() - start > timeout) {
clearInterval(appearanceInterval);
return resolve(false);
}
const notices = document.querySelectorAll('.notice');
for (let i = 0; i < notices.length; i++) {
const el = notices[i];
if (toMatch.test(el.textContent)) {
foundNoticeElement = el;
el.detach();
clearInterval(appearanceInterval);
return resolve(true);
}
}
}, 100);
});
}
async function checkUpdates(tabId) {
app.setting.openTabById(tabId);
await clickButtonLabel("Check for updates");
await waitForNotice(/(No \S+ updates found\.|Found [0-9]+ \S+ to update\.)/i);
await clickButtonLabel("Update all");
}
app.setting.open();
await checkUpdates("community-plugins");
await checkUpdates("appearance");
app.setting.close();
};