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