From 0b1bb6fcd30e1666e3f6b9bc1a0f26d4a171ef49 Mon Sep 17 00:00:00 2001 From: Simon Lecoq <22963968+lowlighter@users.noreply.github.com> Date: Tue, 6 Apr 2021 22:22:03 +0200 Subject: [PATCH] Add markdown template (#209) --- .github/readme/partials/introduction.md | 10 ++++- .github/readme/partials/references.md | 3 +- source/app/metrics/index.mjs | 25 +++++++++++ source/app/metrics/metadata.mjs | 4 +- source/app/web/instance.mjs | 2 +- source/plugins/core/metadata.yml | 20 +++++++-- source/templates/markdown/README.md | 29 ++++++++++++ source/templates/markdown/example.md | 27 ++++++++++++ source/templates/markdown/image.svg | 1 + source/templates/markdown/partials/_.json | 1 + .../templates/markdown/partials/activity.ejs | 44 +++++++++++++++++++ 11 files changed, 158 insertions(+), 8 deletions(-) create mode 100644 source/templates/markdown/README.md create mode 100644 source/templates/markdown/example.md create mode 100644 source/templates/markdown/image.svg create mode 100644 source/templates/markdown/partials/_.json create mode 100644 source/templates/markdown/partials/activity.ejs diff --git a/.github/readme/partials/introduction.md b/.github/readme/partials/introduction.md index 93b5d4b0..9feea18b 100644 --- a/.github/readme/partials/introduction.md +++ b/.github/readme/partials/introduction.md @@ -55,7 +55,7 @@ And you can customize these heavily with plugins, templates and hundreds of opti <% } %> <% { let cell = 0 - const elements = Object.entries(templates).filter(([key, value]) => value) + const elements = Object.entries(templates).filter(([key, value]) => (value)&&(!["community"].includes(key))) %> @@ -83,5 +83,13 @@ And you can customize these heavily with plugins, templates and hundreds of opti <% if (cell === "odd") { -%> <% }}} -%> + + + + + +
<%= templates.community.name -%>
+ <%- templates.community.readme.demo.replace(/ i ? ` ${x}` : x)?.join("\n") %> +
<% } %> diff --git a/.github/readme/partials/references.md b/.github/readme/partials/references.md index ad679906..6f00a3a1 100644 --- a/.github/readme/partials/references.md +++ b/.github/readme/partials/references.md @@ -14,4 +14,5 @@ * [jasonlong/isometric-contributions](https://github.com/jasonlong/isometric-contributions) * [jamesgeorge007/github-activity-readme](https://github.com/jamesgeorge007/github-activity-readme) * [vvo/sourcekarma](https://github.com/vvo/sourcekarma) -* [ryo-ma/github-profile-trophy](https://github.com/ryo-ma/github-profile-trophy) \ No newline at end of file +* [ryo-ma/github-profile-trophy](https://github.com/ryo-ma/github-profile-trophy) +* [teoxoy/profile-readme-stats](https://github.com/teoxoy/profile-readme-stats) \ No newline at end of file diff --git a/source/app/metrics/index.mjs b/source/app/metrics/index.mjs index bade87fe..87d5c681 100644 --- a/source/app/metrics/index.mjs +++ b/source/app/metrics/index.mjs @@ -62,6 +62,31 @@ return {rendered:data, mime:"application/json"} } + //Markdown output + if (convert === "markdown") { + //Retrieving template source + console.debug(`metrics/compute/${login} > markdown render`) + let source = image + try { + let template = q.markdown + if (!/^https:/.test(template)) { + const {data:{default_branch:branch, full_name:repo}} = await rest.repos.get({owner:login, repo:q.repo||login}) + console.debug(`metrics/compute/${login} > on ${repo} with default branch ${branch}`) + template = `https://raw.githubusercontent.com/${repo}/${branch}/${template}` + } + console.debug(`metrics/compute/${login} > fetching ${template}`) + ;({data:source} = await imports.axios.get(template, {headers:{Accept:"text/plain"}})) + } + catch (error) { + console.debug(error) + } + //Rendering template source + let rendered = source.replace(/\{\{ (?[\s\S]*?) \}\}/g, "{%= $ %}") + for (const delimiters of [{openDelimiter:"<", closeDelimiter:">"}, {openDelimiter:"{", closeDelimiter:"}"}]) + rendered = await ejs.render(rendered, {...data, s:imports.s, f:imports.format}, {views, async:true, ...delimiters}) + return {rendered, mime:"text/plain"} + } + //Rendering console.debug(`metrics/compute/${login} > render`) let rendered = await ejs.render(image, {...data, s:imports.s, f:imports.format, style, fonts}, {views, async:true}) diff --git a/source/app/metrics/metadata.mjs b/source/app/metrics/metadata.mjs index 909149ea..18cfd5b0 100644 --- a/source/app/metrics/metadata.mjs +++ b/source/app/metrics/metadata.mjs @@ -43,8 +43,8 @@ Templates[name] = await metadata.template({__templates, name, plugins, logger}) } //Reorder keys - const {classic, repository, community, ...templates} = Templates - Templates = {classic, repository, ...templates, community} + const {classic, repository, markdown, community, ...templates} = Templates + Templates = {classic, repository, ...templates, markdown, community} //Packaged metadata const packaged = JSON.parse(`${await fs.promises.readFile(__package)}`) diff --git a/source/app/web/instance.mjs b/source/app/web/instance.mjs index 39550190..90221ff9 100644 --- a/source/app/web/instance.mjs +++ b/source/app/web/instance.mjs @@ -197,7 +197,7 @@ graphql, rest, plugins, conf, die:q["plugins.errors.fatal"] ?? false, verify:q.verify ?? false, - convert:["jpeg", "png", "json"].includes(q["config.output"]) ? q["config.output"] : null, + convert:["jpeg", "png", "json", "markdown"].includes(q["config.output"]) ? q["config.output"] : null, }, {Plugins, Templates}) //Cache if ((!debug)&&(cached)) { diff --git a/source/plugins/core/metadata.yml b/source/plugins/core/metadata.yml index 85cb7525..c725fad9 100644 --- a/source/plugins/core/metadata.yml +++ b/source/plugins/core/metadata.yml @@ -58,6 +58,19 @@ inputs: type: string default: github-metrics.svg + # Rendered markdown output path (when using a markdown template) + # It can be either a local path or a link (e.g. raw.githubusercontent.com) + markdown: + description: Rendered markdown output path + type: string + default: TEMPLATE.md + + # Rendered markdown file cache (when using a markdown template) + markdown_cache: + description: Rendered markdown file cache + type: string + default: .cache + # Output action output_action: description: Output action @@ -155,9 +168,10 @@ inputs: default: svg values: - svg - - png # Does not support animations - - jpeg # Does not support animations and transparency - - json # Outputs a JSON file instead of an image + - png # Does not support animations + - jpeg # Does not support animations and transparency + - json # Outputs a JSON file instead of an image + - markdown # Outputs a Markdown file instead of an image # Number of retries in case rendering fail retries: diff --git a/source/templates/markdown/README.md b/source/templates/markdown/README.md new file mode 100644 index 00000000..30839628 --- /dev/null +++ b/source/templates/markdown/README.md @@ -0,0 +1,29 @@ +### 📒 Markdown (🚧 v3.7) + +Markdown template can render a **markdown template** by interpreting **templating brackets** `{{` and `}}`. + + + +
+ +
+ +It can be used to render custom markdown which include data gathered by metrics. +Unlike SVG templates, it is possible to include revelant hyperlinks since it'll be rendered as regular markdown. + +You can even mix it with SVG plugins for even more customization. + +See [example.md](/source/templates/markdown/example.md) for a markdown template example. + +#### â„šī¸ Examples workflows + +```yaml +- uses: lowlighter/metrics@latest + with: + # ... other options + template: markdown + filename: README.md # Output file + markdown: TEMPLATE.md # Template file + markdown_cache: .cache # Cache folder + config_output: markdown # Output as markdown file +``` diff --git a/source/templates/markdown/example.md b/source/templates/markdown/example.md new file mode 100644 index 00000000..0bf82ffa --- /dev/null +++ b/source/templates/markdown/example.md @@ -0,0 +1,27 @@ +# 📒 Markdown template example + +This is a markdown template example which explain the basic usage of this template. + +## đŸˆ‚ī¸ Templating syntax: + +* Regular EJS syntax is supported +* `{{` and `}}` will be interpolated as EJS brackets (syntaxic sugar) + * `{%` and `%}` can be used as control statements +* Use [metrics.lecoq.io](https://metrics.lecoq.io/) with `config.output=json` to see available data + * You can also use `config_output: json` in GitHub Actions and/or inspect [metrics](https://github.com/lowlighter/metrics) code to get available data too + +## 🧩 Markdown plugins + +Most of plugins from SVG templates can be reused directly by including image source in markdown, but some have them have their own **markdown** version which includes hyperlinks. + +### 📰 Recent activity + +<%- await include(`partials/activity.ejs`) %> + +### âœ’ī¸ Recent posts + +*Coming soon* + +### đŸ—ŧ Rss feed + +*Coming soon* diff --git a/source/templates/markdown/image.svg b/source/templates/markdown/image.svg new file mode 100644 index 00000000..c951d8d2 --- /dev/null +++ b/source/templates/markdown/image.svg @@ -0,0 +1 @@ +You did not provide a valid "markdown" query parameter, which is required to use this template. \ No newline at end of file diff --git a/source/templates/markdown/partials/_.json b/source/templates/markdown/partials/_.json new file mode 100644 index 00000000..0637a088 --- /dev/null +++ b/source/templates/markdown/partials/_.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/source/templates/markdown/partials/activity.ejs b/source/templates/markdown/partials/activity.ejs new file mode 100644 index 00000000..235f5eaa --- /dev/null +++ b/source/templates/markdown/partials/activity.ejs @@ -0,0 +1,44 @@ +<%_ if (plugins.activity) { _%> + <%_ if (plugins.activity.error) { _%> +<%= plugins.activity.error.message _%> + <%_ } else if (plugins.activity.events.length) { _%> + <%_ for (const {actor, type, repo, timestamp, ...event} of plugins.activity.events) { _%> + <%_ if (type === "comment") { _%> +* đŸ’Ŧ Commented on [#<%= event.number %> <%= event.title %>](https://github.com/<%= repo %>/<%= {issue:"issues", pr:"pulls", commit:"commit"}[event.on] %>/<%= event.number %>) from [<%= repo %>](https://github.com/<%= repo %>) + <%_ } else if (type === "member") { _%> +* đŸ’ŧ Added [<%= event.user %>](https://github.com/<%= event.user %>) as collaborator in [<%= repo %>](https://github.com/<%= repo %>) + <%_ } else if (type === "star") { _%> +* 🌟 Starred [<%= repo %>](https://github.com/<%= repo %>) + <%_ } else if (type === "release") { _%> +* đŸ“Ļ <%- event.draft ? "Drafted release" : event.prerelease ? "Pre-released" : "Released" %> **<%= event.name %>** of [<%= repo %>](https://github.com/<%= repo %>) + <%_ } else if (type === "fork") { _%> +* đŸŊī¸ Forked [<%= repo %>](https://github.com/<%= repo %>) + <%_ } else if (type === "push") { _%> +* âžĄī¸ Pushed <%= event.size %> commit<%= s(event.size) %> in [<%= repo %>](https://github.com/<%= repo %>) <%= event.branch ? `on branch \`${event.branch}\`` : "" %> + <%_ for (const commit of event.commits) { _%> + * [#<%= commit.sha %>](https://github.com/<%= repo %>/commit/<%= commit.sha %>) <%= commit.message %> + <%_ } _%> + <%_ } else if (type === "issue") { _%> +* #ī¸âƒŖ <%- event.action === "opened" ? "Opened" : event.action === "reopened" ? "Reopened" : "Closed" %> [#<%= event.number %> <%= event.title %>](https://github.com/<%= repo %>/issues/<%= event.number %>) in [<%= repo %>](https://github.com/<%= repo %>) + <%_ } else if (type === "pr") { _%> +* 🔃 <%- event.action === "opened" ? "Opened" : "Merged" %> [#<%= event.number %> <%= event.title %>](https://github.com/<%= repo %>/pulls/<%= event.number %>) in [<%= repo %>](https://github.com/<%= repo %>) + * <%= event.files.changed %> file<%= s(event.files.changed) %> changed `++<%= event.lines.added %> --<%= event.lines.deleted%>` + <%_ } else if (type === "wiki") { _%> +* 📝 Updated <%= event.pages.length %> wiki page<%= s(event.pages.length) %> in [<%= repo %>](https://github.com/<%= repo %>/wiki) + <%_ for (const page of event.pages) { _%> + * <%= page %> + <%_ } _%> + <%_ } else if (type === "public") { _%> +* 🚀 Made [<%= repo %>](https://github.com/<%= repo %>) public + <%_ } else if (type === "review") { _%> +* 🔍 Reviewed [#<%= event.number %> <%= event.title %>](https://github.com/<%= repo %>/pulls/<%= event.number %>) in [<%= repo %>](https://github.com/<%= repo %>) + <%_ } else if (type === "ref/create") { _%> +* âēī¸ Created new <%= event.ref.type %> `<%= event.ref.name %>` in [<%= repo %>](https://github.com/<%= repo %>) + <%_ } else if (type === "ref/create") { _%> +* 🚮 Deleted <%= event.ref.type %> `<%= event.ref.name %>` from [<%= repo %>](https://github.com/<%= repo %>) + <%_ } _%> + <%_ } _%> + <%_ } else { _%> +No recent activity + <%_ } _%> +<%_ } _%> \ No newline at end of file