Remove ownerAffiliations: OWNER constraint and add option to include forks (#61)

This commit is contained in:
Simon Lecoq
2021-01-14 13:33:46 +01:00
committed by GitHub
parent 9632f5faf6
commit c34e73fa68
19 changed files with 64 additions and 45 deletions

View File

@@ -1508,6 +1508,18 @@ Is is possible to commit generated metrics in a specific branch rather than defa
committer_branch: my-branch
```
#### 🍴 Including forked repositories
🚧 This feature is available as pre-release on @master branch (unstable)
Is is possible to include forked repositories into generated metrics by adding the following to your workflow:
```yaml
- uses: lowlighter/metrics@master
with:
# ... other options
repositories_forks: yes
```
#### 💱 Convert output to PNG/JPEG
It is possible to convert output from SVG to PNG or JPEG images by adding the following to your workflow:

View File

@@ -84,6 +84,11 @@ inputs:
description: Number of repositories to use
default: 100
# Whether to include forked repositories into metrics
repositories_forks:
description: Include forks in metrics
default: no
# Template to use
# See https://github.com/lowlighter/metrics/tree/master/source/templates for supported templates
template:

View File

@@ -238,7 +238,9 @@
//Repositories to use
const repositories = input.number("repositories")
const forks = input.bool("repositories_forks")
info("Repositories to process", repositories)
info("Include forked repositories", forks)
//Die on plugins errors
const die = input.bool("plugins_errors_fatal")
@@ -247,7 +249,7 @@
//Build query
const query = input.object("query")
info("Query additional params", query)
q = {...query, ...q, base:false, ...base, ...config, repositories, template}
q = {...query, ...q, base:false, ...base, ...config, repositories, "repositories.forks":forks, template}
//Render metrics
const {rendered} = await metrics({login:user, q, dflags}, {graphql, rest, plugins, conf, die, verify}, {Plugins, Templates})

View File

@@ -51,7 +51,8 @@
else {
//Query data from GitHub API
console.debug(`metrics/compute/${login} > graphql query`)
Object.assign(data, await graphql(queries.common({login, "calendar.from":new Date(Date.now()-14*24*60*60*1000).toISOString(), "calendar.to":(new Date()).toISOString()})))
const forks = q["repositories.forks"] || false
Object.assign(data, await graphql(queries.common({login, "calendar.from":new Date(Date.now()-14*24*60*60*1000).toISOString(), "calendar.to":(new Date()).toISOString(), forks:forks ? "" : ", isFork: false"})))
//Query repositories from GitHub API
{
//Iterate through repositories
@@ -59,7 +60,7 @@
let pushed = 0
do {
console.debug(`metrics/compute/${login} > retrieving repositories after ${cursor}`)
const {user:{repositories:{edges, nodes}}} = await graphql(queries.repositories({login, after:cursor ? `after: "${cursor}"` : "", repositories:Math.min(repositories, 100)}))
const {user:{repositories:{edges, nodes}}} = await graphql(queries.repositories({login, after:cursor ? `after: "${cursor}"` : "", repositories:Math.min(repositories, 100), forks:forks ? "" : ", isFork: false"}))
cursor = edges?.[edges?.length-1]?.cursor
data.user.repositories.nodes.push(...nodes)
pushed = nodes.length
@@ -90,7 +91,7 @@
//Template rendering
console.debug(`metrics/compute/${login} > render`)
let rendered = await ejs.render(image, {...data, s, style, fonts}, {views, async:true})
let rendered = await ejs.render(image, {...data, s, f:format, style, fonts}, {views, async:true})
//Apply resizing
const {resized, mime} = await svgresize(rendered, {paddings:q["config.padding"], convert})
rendered = resized
@@ -129,11 +130,11 @@
}
/** Formatter */
function format(n) {
function format(n, {sign = false} = {}) {
for (const {u, v} of [{u:"b", v:10**9}, {u:"m", v:10**6}, {u:"k", v:10**3}])
if (n/v >= 1)
return `${(n/v).toFixed(2).substr(0, 4).replace(/[.]0*$/, "")}${u}`
return n
return `${(sign)&&(n > 0) ? "+" : ""}${(n/v).toFixed(2).substr(0, 4).replace(/[.]0*$/, "")}${u}`
return `${(sign)&&(n > 0) ? "+" : ""}${n}`
}
/** Bytes formatter */

View File

@@ -113,6 +113,7 @@
name:"metrics",
watchers:{totalCount:Math.floor(Math.random()*100)},
stargazers:{totalCount:Math.floor(Math.random()*1000)},
owner:{login:"lowlighter"},
languages:{
edges:[
{size:111733, node:{color:"#f1e05a", name:"JavaScript"}
@@ -141,6 +142,7 @@
user:{
repository:{
name:"metrics",
owner:{login:"lowlighter"},
createdAt:new Date().toISOString(),
diskUsage:Math.floor(Math.random()*10000),
watchers:{totalCount:Math.floor(Math.random()*100)},

View File

@@ -6,11 +6,11 @@
if ((!enabled)||(!q.lines))
return null
//Repositories
const repositories = data.user.repositories.nodes.map(({name}) => name) ?? []
const repositories = data.user.repositories.nodes.map(({name:repo, owner:{login:owner}}) => ({repo, owner})) ?? []
//Get contributors stats from repositories
console.debug(`metrics/compute/${login}/plugins > lines > querying api`)
const lines = {added:0, deleted:0}
const response = await Promise.all(repositories.map(async repo => await rest.repos.getContributorsStats({owner:login, repo})))
const response = await Promise.all(repositories.map(async ({repo, owner}) => await rest.repos.getContributorsStats({owner, repo})))
//Compute changed lines
console.debug(`metrics/compute/${login}/plugins > lines > computing total diff`)
response.map(({data:repository}) => {
@@ -23,9 +23,6 @@
if (contributor)
contributor.weeks.forEach(({a, d}) => (lines.added += a, lines.deleted += d))
})
//Format values
lines.added = imports.format(lines.added)
lines.deleted = imports.format(lines.deleted)
//Results
return lines
}

View File

@@ -47,11 +47,6 @@
}
total.min = Math.min(...Object.values(total.dates))
total.max = Math.max(...Object.values(total.dates))
//Format values
for (const date in increments.dates)
increments.dates[date] = {value:increments.dates[date], text:`${increments.dates[date] > 0 ? "+" : ""}${imports.format(increments.dates[date])}`}
for (const date in total.dates)
total.dates[date] = {value:total.dates[date], text:imports.format(total.dates[date])}
//Months name
const months = ["", "Jan.", "Feb.", "Mar.", "Apr.", "May", "June", "July", "Aug.", "Sep.", "Oct.", "Nov.", "Dec."]
//Results

View File

@@ -14,9 +14,6 @@
const {user:{starredRepositories:{edges:repositories}}} = await graphql(queries.starred({login, limit}))
//Format starred repositories
for (const edge of repositories) {
//Formats values
edge.node.stargazers = imports.format(edge.node.stargazerCount)
edge.node.forks = imports.format(edge.node.forkCount)
//Format date
const time = (Date.now()-new Date(edge.starredAt).getTime())/(24*60*60*1000)
let updated = new Date(edge.starredAt).toDateString().substring(4)

View File

@@ -6,17 +6,14 @@
if ((!enabled)||(!q.traffic))
return null
//Repositories
const repositories = data.user.repositories.nodes.map(({name}) => name) ?? []
const repositories = data.user.repositories.nodes.map(({name:repo, owner:{login:owner}}) => ({repo, owner})) ?? []
//Get views stats from repositories
console.debug(`metrics/compute/${login}/plugins > traffic > querying api`)
const views = {count:0, uniques:0}
const response = await Promise.all(repositories.map(async repo => await rest.repos.getViews({owner:login, repo})))
const response = await Promise.all(repositories.map(async ({repo, owner}) => await rest.repos.getViews({owner, repo})))
//Compute views
console.debug(`metrics/compute/${login}/plugins > traffic > computing stats`)
response.filter(({data}) => data).map(({data:{count, uniques}}) => (views.count += count, views.uniques += uniques))
//Format values
views.count = imports.format(views.count)
views.uniques = imports.format(views.uniques)
//Results
return {views}
}

View File

@@ -8,7 +8,7 @@ query Metrics {
websiteUrl
isHireable
twitterUsername
repositories(last: 0, isFork: false, ownerAffiliations: OWNER) {
repositories(last: 0 $forks) {
totalCount
totalDiskUsage
nodes {

View File

@@ -1,11 +1,15 @@
query Repositories {
user(login: "$login") {
repositories($after first: $repositories, isFork: false, ownerAffiliations: OWNER, orderBy: {field: UPDATED_AT, direction: DESC}) {
repositories($after first: $repositories $forks, orderBy: {field: UPDATED_AT, direction: DESC}) {
edges {
cursor
}
nodes {
name
owner {
login
}
isFork
watchers {
totalCount
}

View File

@@ -2,6 +2,10 @@ query Repository {
user(login: "$login") {
repository(name: "$repo") {
name
owner {
login
}
isFork
createdAt
diskUsage
homepageUrl

View File

@@ -14,6 +14,7 @@ query Starred {
openGraphImageUrl
licenseInfo {
nickname
spdxId
name
}
pullRequests {

View File

@@ -2,7 +2,7 @@
<section>
<h2 class="field">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M2 2.5A2.5 2.5 0 014.5 0h8.75a.75.75 0 01.75.75v12.5a.75.75 0 01-.75.75h-2.5a.75.75 0 110-1.5h1.75v-2h-8a1 1 0 00-.714 1.7.75.75 0 01-1.072 1.05A2.495 2.495 0 012 11.5v-9zm10.5-1V9h-8c-.356 0-.694.074-1 .208V2.5a1 1 0 011-1h8zM5 12.25v3.25a.25.25 0 00.4.2l1.45-1.087a.25.25 0 01.3 0L8.6 15.7a.25.25 0 00.4-.2v-3.25a.25.25 0 00-.25-.25h-3.5a.25.25 0 00-.25.25z"></path></svg>
<%= user.repositories.totalCount %> Repositor<%= s(user.repositories.totalCount, "y") %>
<%= user.repositories.totalCount %> Repositor<%= s(user.repositories.totalCount, "y") %> <%= computed.repositories.forked ? `(including ${computed.repositories.forked} fork${s(computed.repositories.forked)})` : "" %>
</h2>
<div class="row">
<section>
@@ -32,7 +32,7 @@
<% if (plugins.lines.error) { %>
<%= plugins.lines.error.message %>
<% } else { %>
<%= plugins.lines.added %> added, <%= plugins.lines.deleted %> removed
<%= f(plugins.lines.added) %> added, <%= f(plugins.lines.deleted) %> removed
<% } %>
</div>
<% } %>
@@ -60,7 +60,7 @@
<% if (plugins.traffic.error) { %>
<%= plugins.traffic.error.message %>
<% } else { %>
<%= plugins.traffic.views.count %> view<%= s(plugins.traffic.views.count) %> in last two weeks
<%= f(plugins.traffic.views.count) %> view<%= s(plugins.traffic.views.count) %> in last two weeks
<% } %>
</div>
<% } %>

View File

@@ -14,9 +14,9 @@
<section class="column chart">
<h3>Total stargazers</h3>
<div class="chart-bars">
<% { let previous = null; for (const [date, {value, text}] of Object.entries(plugins.stargazers.total.dates)) { const p = 0.05+0.95*(value-plugins.stargazers.total.min)/(plugins.stargazers.total.max-plugins.stargazers.total.min); const [y, m, d] = date.split("-").map(Number) %>
<% { let previous = null; for (const [date, value] of Object.entries(plugins.stargazers.total.dates)) { const p = 0.05+0.95*(value-plugins.stargazers.total.min)/(plugins.stargazers.total.max-plugins.stargazers.total.min); const [y, m, d] = date.split("-").map(Number) %>
<div class="entry">
<span class="value"><%= (value-(previous ?? 0)) ? text : "" %></span>
<span class="value"><%= (value-(previous ?? 0)) ? f(value) : "" %></span>
<div class="bar" style="height: <%= p*50 %>px; background-color: var(--color-calendar-graph-day-L<%= Math.ceil(p/0.25) %>-bg)"></div>
<%= d %>
<% if ((previous === null)||(d === 1)) { %>
@@ -29,9 +29,9 @@
<section class="column chart">
<h3>New stargazers per day</h3>
<div class="chart-bars">
<% { let previous = null; for (const [date, {value, text}] of Object.entries(plugins.stargazers.increments.dates)) { const p = value/plugins.stargazers.increments.max; const [y, m, d] = date.split("-").map(Number) %>
<% { let previous = null; for (const [date, value] of Object.entries(plugins.stargazers.increments.dates)) { const p = value/plugins.stargazers.increments.max; const [y, m, d] = date.split("-").map(Number) %>
<div class="entry">
<span class="value"><%= value != 0 ? text : "" %></span>
<span class="value"><%= value != 0 ? f(value, {sign:true}) : "" %></span>
<div class="bar" style="height: <%= p*50 %>px; background-color: var(--color-calendar-graph-day-L<%= Math.ceil(p/0.25) %>-bg)"></div>
<%= d %>
<% if ((previous === null)||(d === 1)) { %>

View File

@@ -39,24 +39,24 @@
<% if (repository.licenseInfo) { %>
<div>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M8.75.75a.75.75 0 00-1.5 0V2h-.984c-.305 0-.604.08-.869.23l-1.288.737A.25.25 0 013.984 3H1.75a.75.75 0 000 1.5h.428L.066 9.192a.75.75 0 00.154.838l.53-.53-.53.53v.001l.002.002.002.002.006.006.016.015.045.04a3.514 3.514 0 00.686.45A4.492 4.492 0 003 11c.88 0 1.556-.22 2.023-.454a3.515 3.515 0 00.686-.45l.045-.04.016-.015.006-.006.002-.002.001-.002L5.25 9.5l.53.53a.75.75 0 00.154-.838L3.822 4.5h.162c.305 0 .604-.08.869-.23l1.289-.737a.25.25 0 01.124-.033h.984V13h-2.5a.75.75 0 000 1.5h6.5a.75.75 0 000-1.5h-2.5V3.5h.984a.25.25 0 01.124.033l1.29.736c.264.152.563.231.868.231h.162l-2.112 4.692a.75.75 0 00.154.838l.53-.53-.53.53v.001l.002.002.002.002.006.006.016.015.045.04a3.517 3.517 0 00.686.45A4.492 4.492 0 0013 11c.88 0 1.556-.22 2.023-.454a3.512 3.512 0 00.686-.45l.045-.04.01-.01.006-.005.006-.006.002-.002.001-.002-.529-.531.53.53a.75.75 0 00.154-.838L13.823 4.5h.427a.75.75 0 000-1.5h-2.234a.25.25 0 01-.124-.033l-1.29-.736A1.75 1.75 0 009.735 2H8.75V.75zM1.695 9.227c.285.135.718.273 1.305.273s1.02-.138 1.305-.273L3 6.327l-1.305 2.9zm10 0c.285.135.718.273 1.305.273s1.02-.138 1.305-.273L13 6.327l-1.305 2.9z"></path></svg>
<%= repository.licenseInfo.nickname ?? repository.licenseInfo.name %>
<%= repository.licenseInfo.nickname ?? repository.licenseInfo.spdxId ?? repository.licenseInfo.name %>
</div>
<% } %>
<div>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M8 .25a.75.75 0 01.673.418l1.882 3.815 4.21.612a.75.75 0 01.416 1.279l-3.046 2.97.719 4.192a.75.75 0 01-1.088.791L8 12.347l-3.766 1.98a.75.75 0 01-1.088-.79l.72-4.194L.818 6.374a.75.75 0 01.416-1.28l4.21-.611L7.327.668A.75.75 0 018 .25zm0 2.445L6.615 5.5a.75.75 0 01-.564.41l-3.097.45 2.24 2.184a.75.75 0 01.216.664l-.528 3.084 2.769-1.456a.75.75 0 01.698 0l2.77 1.456-.53-3.084a.75.75 0 01.216-.664l2.24-2.183-3.096-.45a.75.75 0 01-.564-.41L8 2.694v.001z"></path></svg>
<%= repository.stargazers %>
<%= f(repository.stargazerCount) %>
</div>
<div>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M5 3.25a.75.75 0 11-1.5 0 .75.75 0 011.5 0zm0 2.122a2.25 2.25 0 10-1.5 0v.878A2.25 2.25 0 005.75 8.5h1.5v2.128a2.251 2.251 0 101.5 0V8.5h1.5a2.25 2.25 0 002.25-2.25v-.878a2.25 2.25 0 10-1.5 0v.878a.75.75 0 01-.75.75h-4.5A.75.75 0 015 6.25v-.878zm3.75 7.378a.75.75 0 11-1.5 0 .75.75 0 011.5 0zm3-8.75a.75.75 0 100-1.5.75.75 0 000 1.5z"></path></svg>
<%= repository.forks %>
<%= f(repository.forkCount) %>
</div>
<div>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM0 8a8 8 0 1116 0A8 8 0 010 8zm9 3a1 1 0 11-2 0 1 1 0 012 0zm-.25-6.25a.75.75 0 00-1.5 0v3.5a.75.75 0 001.5 0v-3.5z"></path></svg>
<%= repository.issues.totalCount %>
<%= f(repository.issues.totalCount) %>
</div>
<div>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z"></path></svg>
<%= repository.pullRequests.totalCount %>
<%= f(repository.pullRequests.totalCount) %>
</div>
</div>
</section>

View File

@@ -2,7 +2,7 @@
export default async function ({login, q, dflags}, {conf, data, rest, graphql, plugins, queries}, {s, pending, imports}) {
//Init
const computed = data.computed = {commits:0, sponsorships:0, licenses:{favorite:"", used:{}}, token:{}, repositories:{watchers:0, stargazers:0, issues_open:0, issues_closed:0, pr_open:0, pr_merged:0, forks:0, releases:0}}
const computed = data.computed = {commits:0, sponsorships:0, licenses:{favorite:"", used:{}}, token:{}, repositories:{watchers:0, stargazers:0, issues_open:0, issues_closed:0, pr_open:0, pr_merged:0, forks:0, forked:0, releases:0}}
const avatar = imports.imgb64(data.user.avatarUrl)
console.debug(`metrics/compute/${login} > formatting common metrics`)
@@ -54,6 +54,8 @@
computed.repositories[property] += repository[property].totalCount
//Forks
computed.repositories.forks += repository.forkCount
if (repository.isFork)
computed.repositories.forked++
//License
if (repository.licenseInfo)
computed.licenses.used[repository.licenseInfo.spdxId] = (computed.licenses.used[repository.licenseInfo.spdxId] ?? 0) + 1

View File

@@ -25,7 +25,7 @@
<% if (plugins.traffic.error) { %>
<%= plugins.traffic.error.message %>
<% } else { %>
<%= plugins.traffic.views.count %> view<%= s(plugins.traffic.views.count) %> in last two weeks
<%= f(plugins.traffic.views.count) %> view<%= s(plugins.traffic.views.count) %> in last two weeks
<% } %>
</div>
<% } %>
@@ -46,7 +46,7 @@
<% if (plugins.lines.error) { %>
<%= plugins.lines.error.message %>
<% } else { %>
<%= plugins.lines.added %> added, <%= plugins.lines.deleted %> removed
<%= f(plugins.lines.added) %> added, <%= f(plugins.lines.deleted) %> removed
<% } %>
</div>
<% } %>

View File

@@ -1,11 +1,11 @@
<% if (base.repositories) { %>
<div class="stdin"><%- meta.$ %> ls -lh github/repositories</div><%# -%>
<div class="stdout"><%# -%>
Total <%= user.repositories.totalCount %> repositor<%= s(user.repositories.totalCount, "y") %> - <%= computed.diskUsage %>
Total <%= user.repositories.totalCount %> repositor<%= s(user.repositories.totalCount, "y") %><%= computed.repositories.forked ? ` - ${computed.repositories.forked} fork${s(computed.repositories.forked)}` : "" %> - <%= computed.diskUsage %>
<% if (plugins.traffic) { if (plugins.traffic.error) { -%>
---- <b> </b> views <span class="error">(<%= plugins.traffic.error.message %>)</span>
<% } else { -%>
-r-- <b><%= `${plugins.traffic.views.count}`.padStart(5) %></b> views
-r-- <b><%= `${f(plugins.traffic.views.count)}`.padStart(5) %></b> views
<% }} -%>
-r-- <b><%= `${computed.repositories.stargazers}`.padStart(5) %></b> stargazer<%= s(computed.repositories.stargazers) %>
-r-- <b><%= `${computed.repositories.forks}`.padStart(5) %></b> fork<%= s(computed.repositories.forks) %>
@@ -28,6 +28,6 @@ dr-x LICENSE
<% } -%>
<% if (plugins.lines) { if (plugins.lines.error) { %>
<span class="diff error">@@ <%= plugins.lines.error.message %> @@</span><% } else { %>
<span class="diff">@@ -<%= plugins.lines.deleted %> +<%= plugins.lines.added %> @@</span>
<span class="diff">@@ -<%= f(plugins.lines.deleted) %> +<%= f(plugins.lines.added) %> @@</span>
<% }} -%>
</div><% } -%>