From 7fecb795f8783c22e3dafb4aa91e6f9c6bb24f2f Mon Sep 17 00:00:00 2001 From: Julio Biason Date: Tue, 2 Jul 2024 14:32:59 -0300 Subject: [PATCH] vault backup: 2024-07-02 14:32:59 --- .obsidian/community-plugins.json | 3 +- .../automatic-table-of-contents/main.js | 291 ++++++++++++++++++ .../automatic-table-of-contents/manifest.json | 10 + .obsidian/workspace.json | 30 +- Kanban Pages/V&V.md | 41 ++- Kanban Pages/Work Kanban.md | 18 +- 6 files changed, 362 insertions(+), 31 deletions(-) create mode 100644 .obsidian/plugins/automatic-table-of-contents/main.js create mode 100644 .obsidian/plugins/automatic-table-of-contents/manifest.json diff --git a/.obsidian/community-plugins.json b/.obsidian/community-plugins.json index ea344c7..7a9692b 100644 --- a/.obsidian/community-plugins.json +++ b/.obsidian/community-plugins.json @@ -1,4 +1,5 @@ [ "obsidian-kanban", - "obsidian-git" + "obsidian-git", + "automatic-table-of-contents" ] \ No newline at end of file diff --git a/.obsidian/plugins/automatic-table-of-contents/main.js b/.obsidian/plugins/automatic-table-of-contents/main.js new file mode 100644 index 0000000..bbc2b0f --- /dev/null +++ b/.obsidian/plugins/automatic-table-of-contents/main.js @@ -0,0 +1,291 @@ +let Plugin = class {} +let MarkdownRenderer = {} +let MarkdownRenderChild = class {} +let htmlToMarkdown = (html) => html + +if (isObsidian()) { + const obsidian = require('obsidian') + Plugin = obsidian.Plugin + MarkdownRenderer = obsidian.MarkdownRenderer + MarkdownRenderChild = obsidian.MarkdownRenderChild + htmlToMarkdown = obsidian.htmlToMarkdown +} + +const codeblockId = 'table-of-contents' +const codeblockIdShort = 'toc' +const availableOptions = { + title: { + type: 'string', + default: '', + comment: '', + }, + style: { + type: 'value', + default: 'nestedList', + values: ['nestedList', 'nestedOrderedList', 'inlineFirstLevel'], + comment: 'TOC style (nestedList|nestedOrderedList|inlineFirstLevel)', + }, + minLevel: { + type: 'number', + default: 0, + comment: 'Include headings from the specified level', + }, + maxLevel: { + type: 'number', + default: 0, + comment: 'Include headings up to the specified level', + }, + includeLinks: { + type: 'boolean', + default: true, + comment: 'Make headings clickable', + }, + debugInConsole: { + type: 'boolean', + default: false, + comment: 'Print debug info in Obsidian console', + }, +} + +class ObsidianAutomaticTableOfContents extends Plugin { + async onload() { + const handler = (sourceText, element, context) => { + context.addChild(new Renderer(this.app, element, context.sourcePath, sourceText)) + } + this.registerMarkdownCodeBlockProcessor(codeblockId, handler) + this.registerMarkdownCodeBlockProcessor(codeblockIdShort, handler) + this.addCommand({ + id: 'insert-automatic-table-of-contents', + name: 'Insert table of contents', + editorCallback: onInsertToc, + }) + this.addCommand({ + id: 'insert-automatic-table-of-contents-docs', + name: 'Insert table of contents (documented)', + editorCallback: onInsertTocWithDocs, + }) + } +} + +function onInsertToc(editor) { + const markdown = '```' + codeblockId + '\n```' + editor.replaceRange(markdown, editor.getCursor()) +} + +function onInsertTocWithDocs(editor) { + let markdown = ['```' + codeblockId] + Object.keys(availableOptions).forEach((optionName) => { + const option = availableOptions[optionName] + const comment = option.comment.length > 0 ? ` # ${option.comment}` : '' + markdown.push(`${optionName}: ${option.default}${comment}`) + }) + markdown.push('```') + editor.replaceRange(markdown.join('\n'), editor.getCursor()) +} + +class Renderer extends MarkdownRenderChild { + constructor(app, element, sourcePath, sourceText) { + super(element) + this.app = app + this.element = element + this.sourcePath = sourcePath + this.sourceText = sourceText + } + + // Render on load + onload() { + this.render() + this.registerEvent(this.app.metadataCache.on('changed', this.onMetadataChange.bind(this))) + } + + // Render on file change + onMetadataChange() { + this.render() + } + + render() { + try { + const options = parseOptionsFromSourceText(this.sourceText) + if (options.debugInConsole) debug('Options', options) + + const metadata = this.app.metadataCache.getCache(this.sourcePath) + const headings = metadata && metadata.headings ? metadata.headings : [] + if (options.debugInConsole) debug('Headings', headings) + + const markdown = getMarkdownFromHeadings(headings, options) + if (options.debugInConsole) debug('Markdown', markdown) + + this.element.empty() + MarkdownRenderer.renderMarkdown(markdown, this.element, this.sourcePath, this) + } catch(error) { + const readableError = `_💥 Could not render table of contents (${error.message})_` + MarkdownRenderer.renderMarkdown(readableError, this.element, this.sourcePath, this) + } + } +} + +function getMarkdownFromHeadings(headings, options) { + const markdownHandlersByStyle = { + nestedList: getMarkdownNestedListFromHeadings, + nestedOrderedList: getMarkdownNestedOrderedListFromHeadings, + inlineFirstLevel: getMarkdownInlineFirstLevelFromHeadings, + } + let markdown = '' + if (options.title && options.title.length > 0) { + markdown += options.title + '\n' + } + const noHeadingMessage = '_Table of contents: no headings found_' + markdown += markdownHandlersByStyle[options.style](headings, options) || noHeadingMessage + return markdown +} + +function getMarkdownNestedListFromHeadings(headings, options) { + return getMarkdownListFromHeadings(headings, false, options) +} + +function getMarkdownNestedOrderedListFromHeadings(headings, options) { + return getMarkdownListFromHeadings(headings, true, options) +} + +function getMarkdownListFromHeadings(headings, isOrdered, options) { + const prefix = isOrdered ? '1.' : '-' + const lines = [] + const minLevel = options.minLevel > 0 + ? options.minLevel + : Math.min(...headings.map((heading) => heading.level)) + headings.forEach((heading) => { + if (heading.level < minLevel) return + if (options.maxLevel > 0 && heading.level > options.maxLevel) return + lines.push(`${'\t'.repeat(heading.level - minLevel)}${prefix} ${getMarkdownHeading(heading, options)}`) + }) + return lines.length > 0 ? lines.join('\n') : null +} + +function getMarkdownInlineFirstLevelFromHeadings(headings, options) { + const minLevel = options.minLevel > 0 + ? options.minLevel + : Math.min(...headings.map((heading) => heading.level)) + const items = headings + .filter((heading) => heading.level === minLevel) + .map((heading) => { + return getMarkdownHeading(heading, options) + }) + return items.length > 0 ? items.join(' | ') : null +} + +function getMarkdownHeading(heading, options) { + const stripMarkdown = (text) => { + text = text.replaceAll('*', '').replaceAll('_', '').replaceAll('`', '') + text = text.replaceAll('==', '').replaceAll('~~', '') + text = text.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1') // Strip markdown links + return text + } + const stripHtml = (text) => stripMarkdown(htmlToMarkdown(text)) + const stripWikilinks = (text, isForLink) => { + // Strip [[link|text]] format + // For the text part of the final link we only keep "text" + // For the link part we need the text + link + // Example: "# Some [[file.md|heading]]" must be translated to "[[#Some file.md heading|Some heading]]" + text = text.replace(/\[\[([^\]]+)\|([^\]]+)\]\]/g, isForLink ? '$1 $2' : '$2') + text = text.replace(/\[\[([^\]]+)\]\]/g, '$1') // Strip [[link]] format + // Replace malformed links & reserved wikilinks chars + text = text.replaceAll('[[', '').replaceAll('| ', isForLink ? '' : '- ').replaceAll('|', isForLink ? ' ' : '-') + return text + } + const stripTags = (text) => text.replaceAll('#', '') + if (options.includeLinks) { + // Remove markdown, HTML & wikilinks from text for readability, as they are not rendered in a wikilink + let text = heading.heading + text = stripMarkdown(text) + text = stripHtml(text) + text = stripWikilinks(text, false) + // Remove wikilinks & tags from link or it won't be clickable (on the other hand HTML & markdown must stay) + let link = heading.heading + link = stripWikilinks(link, true) + link = stripTags(link) + + // Return wiklink style link + return `[[#${link}|${text}]]` + // Why not markdown links? Because even if it looks like the text part would have a better compatibility + // with complex headings (as it would support HTML, markdown, etc) the link part is messy, + // because it requires some encoding that looks buggy and undocumented; official docs state the link must be URL encoded + // (https://help.obsidian.md/Linking+notes+and+files/Internal+links#Supported+formats+for+internal+links) + // but it doesn't work properly, example: "## Some heading with simple HTML" must be encoded as: + // [Some heading with simple HTML](#Some%20heading%20with%20simpler%20HTML) + // and not + // [Some heading with simple HTML](#Some%20%3Cem%3Eheading%3C%2Fem%3E%20with%20simpler%20HTML) + // Also it won't be clickable at all if the heading contains #tags or more complex HTML + // (example: ## Some heading #with-a-tag) + // (unless there is a way to encode these use cases that I didn't find) + } + return heading.heading +} + +function parseOptionsFromSourceText(sourceText = '') { + const options = {} + Object.keys(availableOptions).forEach((option) => { + options[option] = availableOptions[option].default + }) + sourceText.split('\n').forEach((line) => { + const option = parseOptionFromSourceLine(line) + if (option !== null) { + options[option.name] = option.value + } + }) + return options +} + +function parseOptionFromSourceLine(line) { + const matches = line.match(/([a-zA-Z0-9._ ]+):(.*)/) + if (line.startsWith('#') || !matches) return null + const possibleName = matches[1].trim() + const optionParams = availableOptions[possibleName] + let possibleValue = matches[2].trim() + if (!optionParams || optionParams.type !== 'string') { + // Strip comments from values except for strings (as a string may contain markdown) + possibleValue = possibleValue.replace(/#[^#]*$/, '').trim() + } + const valueError = new Error(`Invalid value for \`${possibleName}\``) + if (optionParams && optionParams.type === 'number') { + const value = parseInt(possibleValue) + if (value < 0) throw valueError + return { name: possibleName, value } + } + if (optionParams && optionParams.type === 'boolean') { + if (!['true', 'false'].includes(possibleValue)) throw valueError + return { name: possibleName, value: possibleValue === 'true' } + } + if (optionParams && optionParams.type === 'value') { + if (!optionParams.values.includes(possibleValue)) throw valueError + return { name: possibleName, value: possibleValue } + } + if (optionParams && optionParams.type === 'string') { + return { name: possibleName, value: possibleValue } + } + return null +} + +function debug(type, data) { + console.log(...[ + `%cAutomatic Table Of Contents %c${type}:\n`, + 'color: orange; font-weight: bold', + 'font-weight: bold', + data, + ]) +} + +function isObsidian() { + if (typeof process !== 'object') { + return true // Obsidian mobile doesn't have a global process object + } + return !process.env || !process.env.JEST_WORKER_ID // Jest runtime is not Obsidian +} + +if (isObsidian()) { + module.exports = ObsidianAutomaticTableOfContents +} else { + module.exports = { + parseOptionsFromSourceText, + getMarkdownFromHeadings, + } +} diff --git a/.obsidian/plugins/automatic-table-of-contents/manifest.json b/.obsidian/plugins/automatic-table-of-contents/manifest.json new file mode 100644 index 0000000..535186f --- /dev/null +++ b/.obsidian/plugins/automatic-table-of-contents/manifest.json @@ -0,0 +1,10 @@ +{ + "id": "automatic-table-of-contents", + "name": "Automatic Table Of Contents", + "version": "1.4.0", + "minAppVersion": "1.3.0", + "description": "Create a table of contents in a note, that updates itself when the note changes", + "author": "Johan Satgé", + "authorUrl": "https://github.com/johansatge", + "isDesktopOnly": false +} \ No newline at end of file diff --git a/.obsidian/workspace.json b/.obsidian/workspace.json index 710224d..d95b74e 100644 --- a/.obsidian/workspace.json +++ b/.obsidian/workspace.json @@ -13,19 +13,7 @@ "state": { "type": "markdown", "state": { - "file": "Meetings/DevOps/2024-06-01.md", - "mode": "source", - "source": false - } - } - }, - { - "id": "baccd079c408fe2f", - "type": "leaf", - "state": { - "type": "markdown", - "state": { - "file": "Kanban Pages/HelyxVerify.md", + "file": "Kanban Pages/V&V.md", "mode": "source", "source": false } @@ -97,7 +85,7 @@ "state": { "type": "backlink", "state": { - "file": "Meetings/DevOps/2024-06-01.md", + "file": "Kanban Pages/V&V.md", "collapseAll": true, "extraContext": false, "sortOrder": "alphabetical", @@ -114,7 +102,7 @@ "state": { "type": "outgoing-link", "state": { - "file": "Meetings/DevOps/2024-06-01.md", + "file": "Kanban Pages/V&V.md", "linksCollapsed": false, "unlinkedCollapsed": true } @@ -137,7 +125,7 @@ "state": { "type": "outline", "state": { - "file": "Meetings/DevOps/2024-06-01.md" + "file": "Kanban Pages/V&V.md" } } } @@ -158,16 +146,16 @@ "obsidian-kanban:Create new board": false } }, - "active": "2f56f8d2c998700c", + "active": "676a3e53a7c36034", "lastOpenFiles": [ - "Meetings/QA/2024-07-01.md", + "Kanban Pages/Work Kanban.md", + "Kanban Pages/V&V.md", + "Kanban Pages/HelyxVerify.md", "Meetings/DevOps/2024-06-01.md", + "Meetings/QA/2024-07-01.md", "Meetings/DevOps", - "Kanban Pages/Work Kanban.md", "Meetings/QA", "Meetings", - "Kanban Pages/HelyxVerify.md", - "Kanban Pages/V&V.md", "Kanban Pages/QA.md", "Windows Compilation.md", "Kanban Pages/Windows Native Compilation.md", diff --git a/Kanban Pages/V&V.md b/Kanban Pages/V&V.md index dfe9327..50911ec 100644 --- a/Kanban Pages/V&V.md +++ b/Kanban Pages/V&V.md @@ -1,3 +1,11 @@ +```table-of-contents +title: +style: nestedList # TOC style (nestedList|nestedOrderedList|inlineFirstLevel) +minLevel: 0 # Include headings from the specified level +maxLevel: 0 # Include headings up to the specified level +includeLinks: true # Make headings clickable +debugInConsole: false # Print debug info in Obsidian console +``` # Docker Container `docker build . -t vv:latest` @@ -7,12 +15,13 @@ - Core: `/core` `vv init --collection_path=/collection/` - -## Error 1 +# Pipeline +Looks like AndrewC is also working on something like this. +# Error 1 `CommandSequence.__init__() got an unexpected keyword argument 'mode'` Found out that CommandSequence doesn't have a "mode" parameter, but all the configurations in vv-collection have it in their commands. Fix was to add the property, although it's clear things are now broken -## Error 2 +# Error 2 ``` Traceback (most recent call last): File "/usr/local/bin/vv", line 8, in @@ -43,7 +52,7 @@ Traceback (most recent call last): AttributeError: 'dict' object has no attribute 'parameters' ``` -## Ruan +# Ruan [2:24 PM] Ruan Engelbrecht Ah yes so at the bottom there's the list of cases specified under the run_cases key @@ -54,4 +63,26 @@ Uncomment the last two lines of my file, and source the mid-sized-ci collection, [2:26 PM] Ruan Engelbrecht -And source your core \ No newline at end of file +And source your core +# MPI in docker +mpirun has detected an attempt to run as root. + +Running as root is *strongly* discouraged as any mistake (e.g., in +defining TMPDIR) or bug can result in catastrophic damage to the OS +file system, leaving your system in an unusable state. + +We strongly suggest that you run mpirun as a non-root user. + +You can override this protection by adding the --allow-run-as-root option +to the cmd line or by setting two environment variables in the following way: +the variable OMPI_ALLOW_RUN_AS_ROOT=1 to indicate the desire to override this +protection, and OMPI_ALLOW_RUN_AS_ROOT_CONFIRM=1 to confirm the choice and +add one more layer of certainty that you want to do so. +We reiterate our advice against doing so - please proceed at your own risk. +# HPC03 machine +$ cat /etc/centos-release +CentOS Linux release 7.9.2009 (Core) +# Important collections +- customers_report +- engys/automotive +- mid-sized-ci \ No newline at end of file diff --git a/Kanban Pages/Work Kanban.md b/Kanban Pages/Work Kanban.md index a6e4aa6..80e44c4 100644 --- a/Kanban Pages/Work Kanban.md +++ b/Kanban Pages/Work Kanban.md @@ -31,13 +31,13 @@ kanban-plugin: board ## Working +- [ ] Create V&V pipeline + [[V&V]] + #jenkins#vv - [ ] Allow helyxVerify cases not to be reduced https://engys.atlassian.net/browse/DO-1198 [[HelyxVerify#Not reducing]] #helyxVerify -- [ ] Create V&V pipeline - [[V&V]] - #jenkins - [ ] verificationDict [[HelyxVerify#system/validationDict]] #helyxVerify @@ -53,6 +53,16 @@ kanban-plugin: board [[Windows Native Compilation]] +## DevOps Dec24 - A7 + +- [ ] Run all cases to completion, instead of stopping in the first failure. + https://engys.atlassian.net/browse/DO-1211 + #qa +- [ ] On nightly, change the version to have the GUI commit + https://engys.atlassian.net/browse/DO-1210 + #packager + + ## DevOps Dec24 - A6 - [ ] Python Testing Pipeline @@ -92,6 +102,6 @@ kanban-plugin: board %% kanban:settings ``` -{"kanban-plugin":"board","list-collapse":[false,false,false,false]} +{"kanban-plugin":"board","list-collapse":[false,false,false,false,true]} ``` %% \ No newline at end of file