Better organization support (#81)

This commit is contained in:
Simon Lecoq
2021-01-26 18:16:48 +01:00
committed by GitHub
parent 1c8dcbc8aa
commit 76e0013f00
10 changed files with 202 additions and 127 deletions

View File

@@ -563,26 +563,26 @@ The default template is `classic`.
<table> <table>
<tr> <tr>
<th>Template\Plugin</th> <th nowrap="nowrap">Template\Plugin</th>
<th><span title="Base content">🗃️</span></th> <th nowrap="nowrap"><span title="Base content">🗃️<sup>Ø<sup>P</sup></sup></span></th>
<th><span title="PageSpeed">⏱️</span></th> <th nowrap="nowrap"><span title="PageSpeed">⏱️</span></th>
<th><span title="Isometric calendar">📅</span></th> <th nowrap="nowrap"><span title="Isometric calendar">📅<sup>Ø</sup></span></th>
<th><span title="Music">🎼</span></th> <th nowrap="nowrap"><span title="Music">🎼</span></th>
<th><span title="Languages">🈷️</span></th> <th nowrap="nowrap"><span title="Languages">🈷️</span></th>
<th><span title="Follow-up">🎟️</span></th> <th nowrap="nowrap"><span title="Follow-up">🎟️</span></th>
<th><span title="Topics">📌</span></th> <th nowrap="nowrap"><span title="Topics">📌<sup>Ø</sup></span></th>
<th><span title="Projects">🗂️</span></th> <th nowrap="nowrap"><span title="Projects">🗂️</span></th>
<th><span title="Lines">👨‍💻</span></th> <th nowrap="nowrap"><span title="Lines">👨‍💻</span></th>
<th><span title="Traffic">🧮</span></th> <th nowrap="nowrap"><span title="Traffic">🧮</span></th>
<th><span title="Tweets">🐤</span></th> <th nowrap="nowrap"><span title="Tweets">🐤</span></th>
<th><span title="Posts">✒️</span></th> <th nowrap="nowrap"><span title="Posts">✒️</span></th>
<th><span title="Habits">💡</span></th> <th nowrap="nowrap"><span title="Habits">💡</span></th>
<th><span title="Activity">📰</span></th> <th nowrap="nowrap"><span title="Activity">📰</span></th>
<th><span title="Stars">🌟</span></th> <th nowrap="nowrap"><span title="Stars">🌟<sup>Ø</sup></span></th>
<th><span title="Stargazers">✨</span></th> <th nowrap="nowrap"><span title="Stargazers">✨</span></th>
<th><span title="Gists">🎫</span></th> <th nowrap="nowrap"><span title="Gists">🎫<sup>Ø</sup></span></th>
<th><span title="People">🧑‍🤝‍🧑</span></th> <th nowrap="nowrap"><span title="People">🧑‍🤝‍🧑</span></th>
<th><span title="Anilist">🌸</span></th> <th nowrap="nowrap"><span title="Anilist">🌸</span></th>
</tr> </tr>
<tr> <tr>
<th>Classic</th> <th>Classic</th>
@@ -656,7 +656,9 @@ The default template is `classic`.
* **P**: Partial support *(Hover cell for more informations)* * **P**: Partial support *(Hover cell for more informations)*
* **M**: Feature is not released yet but is available on `@master` * **M**: Feature is not released yet but is available on `@master`
* **N**: Feature is already released, but new ones are available on `@master` * **N**: Feature is already released, but new ones are available on `@master`
* **R**: Repository template (all plugins content will be restricted to related repository) * **R**: Repository template (all plugins content will be scoped to related repository)
* **Ø**: Feature is not supported for organization accounts
* **Ø<sup>P</sup>**: Feature is supported partially for organization accounts
<details> <details>
<summary>💬 Using community templates</summary> <summary>💬 Using community templates</summary>
@@ -712,6 +714,36 @@ Add the following to your workflow:
</details> </details>
<details>
<summary>💬 Generating metrics for organizations</summary>
🚧 This feature is available as pre-release on @master branch (unstable)
It is also possible to generate metrics for organization accounts.
Setup is the same as for user accounts (i.e. a personal token from an user account and use of `GITHUB_TOKEN` for commits) but you'll need to change `user` option to your organization name.
Additionally, you'll need to add the `read:org` scope to your personal token, *whether you're member of target organization or not*.
![Add read:org scope to personal token](.github/readme/imgs/setup_token_org_read_scope.png)
Resulting workflow should look like below:
```yaml
- uses: lowlighter/metrics@master
with:
# ... other options
token: ${{ secrets.METRICS_TOKEN }} # A personal token from an user account with read:org scope
committer_token: ${{ secrets.GITHUB_TOKEN }}
user: "organization-name"
```
You may also need to [authorize your personal token](https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/authorizing-a-personal-access-token-for-use-with-saml-single-sign-on) if you're using single sign-on and are encounting errors.
Note that `repositories` option will be capped to 25 repositories to ensure that GraphQL queries does not timeout, so you may end up using more requests than for user accounts.
Although some plugins may be noted as compatible with an organization account, it may not be actually possible to run them successfully depending of your organization size. As some of plugins use a lot of requests, you'll eventually reach the rate-limiter before all of your metrics are generated for large organizations.
</details>
## 🧩 Plugins ## 🧩 Plugins
Plugins are features which can provide additional metrics and features. Plugins are features which can provide additional metrics and features.

View File

@@ -122,7 +122,7 @@
let pushed = 0 let pushed = 0
do { do {
console.debug(`metrics/compute/${login}/common > retrieving repositories after ${cursor}`) console.debug(`metrics/compute/${login}/common > retrieving repositories after ${cursor}`)
const {[account]:{repositories:{edges, nodes}}} = await graphql(queries.repositories({login, account, after:cursor ? `after: "${cursor}"` : "", repositories:Math.min(repositories, 100), forks:forks ? "" : ", isFork: false"})) const {[account]:{repositories:{edges, nodes}}} = await graphql(queries.repositories({login, account, after:cursor ? `after: "${cursor}"` : "", repositories:Math.min(repositories, {user:100, organization:25}[account]), forks:forks ? "" : ", isFork: false"}))
cursor = edges?.[edges?.length-1]?.cursor cursor = edges?.[edges?.length-1]?.cursor
data.user.repositories.nodes.push(...nodes) data.user.repositories.nodes.push(...nodes)
pushed = nodes.length pushed = nodes.length

View File

@@ -24,6 +24,8 @@ query MetricsOrganization {
sponsorshipsAsMaintainer { sponsorshipsAsMaintainer {
totalCount totalCount
} }
membersWithRole {
totalCount
}
} }
} }

View File

@@ -11,7 +11,9 @@
<% if (base.metadata) { %> <% if (base.metadata) { %>
<footer> <footer>
<% if (account === "user") { %>
<span>These metrics <%= !computed.token.scopes.includes("repo") ? "do not include all" : "include" %> private contributions<% if ((config.timezone?.name)&&(!config.timezone?.error)) { %>, timezone <%= config.timezone.name %><% } %></span> <span>These metrics <%= !computed.token.scopes.includes("repo") ? "do not include all" : "include" %> private contributions<% if ((config.timezone?.name)&&(!config.timezone?.error)) { %>, timezone <%= config.timezone.name %><% } %></span>
<% } %>
<span>Last updated <%= new Date().toGMTString() %> with lowlighter/metrics@<%= meta.version %></span> <span>Last updated <%= new Date().toGMTString() %> with lowlighter/metrics@<%= meta.version %></span>
</footer> </footer>
<% } %> <% } %>

Before

Width:  |  Height:  |  Size: 977 B

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -1,4 +1,5 @@
<div class="row"> <% if (account === "user") { %>
<div class="row">
<% if (base.activity) { %> <% if (base.activity) { %>
<section> <section>
<h2 class="field"> <h2 class="field">
@@ -54,4 +55,5 @@
</div> </div>
</section> </section>
<% } %> <% } %>
</div> </div>
<% } %>

View File

@@ -1,4 +1,5 @@
<% if (base.header) { %> <% if (base.header) { %>
<% if (account === "user") { %>
<section> <section>
<h1 class="field"> <h1 class="field">
<img class="avatar" src="data:image/png;base64,<%= computed.avatar %>" width="20" height="20" /> <img class="avatar" src="data:image/png;base64,<%= computed.avatar %>" width="20" height="20" />
@@ -43,4 +44,31 @@
</section> </section>
</div> </div>
</section> </section>
<% } else if (account === "organization") { %>
<section>
<h1 class="field">
<img class="avatar organization" src="data:image/png;base64,<%= computed.avatar %>" width="20" height="20" />
<span><%= user.name || user.login %></span>
</h1>
<div class="row">
<section>
<div class="field <%= computed.cakeday ? 'cakeday' : '' %>">
<% if (computed.cakeday) { %>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M4.75 1.5a1.25 1.25 0 100 2.5h2.309c-.233-.818-.542-1.401-.878-1.793-.43-.502-.915-.707-1.431-.707zM2 2.75c0 .45.108.875.3 1.25h-.55A1.75 1.75 0 000 5.75v2c0 .698.409 1.3 1 1.582v4.918c0 .966.784 1.75 1.75 1.75h10.5A1.75 1.75 0 0015 14.25V9.332c.591-.281 1-.884 1-1.582v-2A1.75 1.75 0 0014.25 4h-.55a2.75 2.75 0 00-2.45-4c-.984 0-1.874.42-2.57 1.23A5.086 5.086 0 008 2.274a5.086 5.086 0 00-.68-1.042C6.623.42 5.733 0 4.75 0A2.75 2.75 0 002 2.75zM8.941 4h2.309a1.25 1.25 0 100-2.5c-.516 0-1 .205-1.43.707-.337.392-.646.975-.879 1.793zm-1.84 1.5H1.75a.25.25 0 00-.25.25v2c0 .138.112.25.25.25h5.5V5.5h-.149zm1.649 0V8h5.5a.25.25 0 00.25-.25v-2a.25.25 0 00-.25-.25h-5.5zm0 4h4.75v4.75a.25.25 0 01-.25.25h-4.5v-5zm-1.5 0v5h-4.5a.25.25 0 01-.25-.25V9.5h4.75z"></path></svg>
Joined GitHub <%= computed.registration %>
<% } else { %>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0zM8 0a8 8 0 100 16A8 8 0 008 0zm.5 4.75a.75.75 0 00-1.5 0v3.5a.75.75 0 00.471.696l2.5 1a.75.75 0 00.557-1.392L8.5 7.742V4.75z"></path></svg>
Joined GitHub <%= computed.registration %>
<% } %>
</div>
</section>
<section>
<div class="field">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M5.5 3.5a2 2 0 100 4 2 2 0 000-4zM2 5.5a3.5 3.5 0 115.898 2.549 5.507 5.507 0 013.034 4.084.75.75 0 11-1.482.235 4.001 4.001 0 00-7.9 0 .75.75 0 01-1.482-.236A5.507 5.507 0 013.102 8.05 3.49 3.49 0 012 5.5zM11 4a.75.75 0 100 1.5 1.5 1.5 0 01.666 2.844.75.75 0 00-.416.672v.352a.75.75 0 00.574.73c1.2.289 2.162 1.2 2.522 2.372a.75.75 0 101.434-.44 5.01 5.01 0 00-2.56-3.012A3 3 0 0011 4z"></path></svg>
<%= user.membersWithRole.totalCount %> member<%= s(user.membersWithRole.totalCount) %>
</div>
</section>
</div>
</section>
<% } %>
<% } %> <% } %>

View File

@@ -89,6 +89,10 @@
margin: 0 6px; margin: 0 6px;
} }
.avatar.organization {
border-radius: 15%;
}
/* Commit calendar */ /* Commit calendar */
.calendar.field { .calendar.field {
margin: 4px 0; margin: 4px 0;

View File

@@ -75,7 +75,7 @@
const years = Math.floor(diff) const years = Math.floor(diff)
const months = Math.floor((diff-years)*12) const months = Math.floor((diff-years)*12)
computed.registration = years ? `${years} year${s(years)} ago` : months ? `${months} month${s(months)} ago` : `${Math.ceil(diff*365)} day${s(Math.ceil(diff*365))} ago` computed.registration = years ? `${years} year${s(years)} ago` : months ? `${months} month${s(months)} ago` : `${Math.ceil(diff*365)} day${s(Math.ceil(diff*365))} ago`
computed.cakeday = [new Date(), new Date(data.user.createdAt)].map(date => date.toISOString().match(/(?<mmdd>\d{2}-\d{2})(?=T)/)?.groups?.mmdd).every((v, _, a) => v === a[0]) computed.cakeday = years > 1 ? [new Date(), new Date(data.user.createdAt)].map(date => date.toISOString().match(/(?<mmdd>\d{2}-\d{2})(?=T)/)?.groups?.mmdd).every((v, _, a) => v === a[0]) : false
//Compute calendar //Compute calendar
computed.calendar = data.user.calendar.contributionCalendar.weeks.flatMap(({contributionDays}) => contributionDays).slice(0, 14).reverse() computed.calendar = data.user.calendar.contributionCalendar.weeks.flatMap(({contributionDays}) => contributionDays).slice(0, 14).reverse()

View File

@@ -1,4 +1,4 @@
<% if ((base.activity)||(base.community)) { %> <% if ((account === "user")&&((base.activity)||(base.community))) { %>
<div class="stdin"><%- meta.$ %> git status</div><%# -%> <div class="stdin"><%- meta.$ %> git status</div><%# -%>
<div class="stdout"><%# -%> <div class="stdout"><%# -%>
<% if (base.activity) { -%> <% if (base.activity) { -%>

View File

@@ -1,7 +1,12 @@
<% if (base.header) { %> <% if ((account === "user")&&(base.header)) { %>
<div class="stdin"><%- meta.$ %> whoami</div><%# -%> <div class="stdin"><%- meta.$ %> whoami</div><%# -%>
<div class="stdout"><%# -%> <div class="stdout"><%# -%>
<b><%= user.name || user.login %></b> registered=<%= computed.registration.match(/^.+? [ymd]/)?.[0].replace(/ /g, "") %>, uid=<%= `${user.databaseId}`.substr(-4) %>, gid=<%= user.organizations.totalCount %> <b><%= user.name || user.login %></b> registered=<%= computed.registration.match(/^.+? [ymd]/)?.[0].replace(/ /g, "") %>, uid=<%= `${user.databaseId}`.substr(-4) %>, gid=<%= user.organizations.totalCount %>
contributed to <%= user.repositoriesContributedTo.totalCount %> repositor<%= s(user.repositoriesContributedTo.totalCount, "y") %> <b><% for (const [x, {color}] of Object.entries(computed.calendar)) { -%><span style="color:<%= color %>">#</span><% } %></b> contributed to <%= user.repositoriesContributedTo.totalCount %> repositor<%= s(user.repositoriesContributedTo.totalCount, "y") %> <b><% for (const [x, {color}] of Object.entries(computed.calendar)) { -%><span style="color:<%= color %>">#</span><% } %></b>
followed by <b><%= user.followers.totalCount %></b> user<%= s(user.followers.totalCount) %> followed by <b><%= user.followers.totalCount %></b> user<%= s(user.followers.totalCount) %>
</div><% } else if ((account === "organization")&&(base.header)) { %><%# -%>
<div class="stdin"><%- meta.$ %> whoami</div><%# -%>
<div class="stdout"><%# -%>
<b><%= user.name || user.login %></b> registered=<%= computed.registration.match(/^.+? [ymd]/)?.[0].replace(/ /g, "") %>, uid=0, gid=<%= `${user.databaseId}`.substr(-4) %>
organization with <b><%= user.membersWithRole.totalCount %></b> member<%= s(user.membersWithRole.totalCount) %>
</div><% } -%> </div><% } -%>