Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
### Category
<!-- Bugfix / Feature / Docs / Dependencies / Localization / Widget / Theme / Showcase -->
<!-- Bugfix / Feature / Docs / Dependencies / Localization / Widget / Theme / Showcase / CI -->

### Overview
<!-- Briefly outline your PR. Explain what was changed and why -->
Expand Down
26 changes: 26 additions & 0 deletions .github/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Groups merged PRs into sections in the auto-generated release notes,
# by the category labels applied in .github/workflows/label-pr.yml
# The first category a PR's labels match wins, so order matters here.

changelog:
categories:
- title: ✨ New Features
labels: ['✨ New Feature']
- title: 🦋 Bug Fixes
labels: ['🦋 Bug Fix', '🚑 Hot Fix']
- title: 🔌 Widgets
labels: ['🔌 Widget']
- title: 🎨 Themes and Styling
labels: ['🎨 Theme', '💄 Stylistic Changes']
- title: 🌐 Localization
labels: ['🌐 Language']
- title: 💯 Showcase
labels: ['💯 Showcase']
- title: 📕 Documentation
labels: ['📕 Docs']
- title: 🛠️ Build, CI and Refactors
labels: ['🛠️ Build Changes', '🚚 Refactor', '🏗️ Architectural Changes']
- title: 📦 Dependencies
labels: ['📦 Dependencies', 'dependencies']
- title: 🌈 Other Changes
labels: ['*']
70 changes: 70 additions & 0 deletions .github/workflows/label-pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Adds labels to PR based on category or files changed
# So that release notes can be categorized by change type

name: 🏷️ Label

on:
pull_request_target: # zizmor: ignore[dangerous-triggers]
types: [opened, edited]

concurrency:
group: label-pr-${{ github.event.pull_request.number }}
cancel-in-progress: true

permissions:
contents: read
pull-requests: write

jobs:
label:
name: 🏷️ Apply Category Label
runs-on: ubuntu-latest
continue-on-error: true
steps:
- name: Determine and Apply Label
uses: actions/github-script@v9
with:
script: |
// [ label, matches Category field, matches changed file paths ]
const categories = [
['🌐 Language', /localis|localiz|translat|i18n|language/i, /^src\/assets\/locales\//],
['🔌 Widget', /widget/i, /^src\/(components|styles)\/[Ww]idgets\/|^docs\/widgets\.md$/],
['🎨 Theme', /theme|theming/i, /^src\/styles\/(themes\/|color-themes\.scss$)|^docs\/theming\.md$/],
['💯 Showcase', /showcase/i, /^docs\/showcase/],
['📦 Dependencies', /dependenc|deps/i, /^(yarn\.lock|package\.json)$/],
['🛠️ Build Changes', /build|\bci\b|workflow/i, /^\.github\/|^Dockerfile$|^vite\.config\.mjs$/],
['📕 Docs', /doc/i, /^docs\/|\.md$/],
['🦋 Bug Fix', /bug ?fix|^fix/i, null],
['✨ New Feature', /feature|feat/i, null],
];

const pr = context.payload.pull_request;
const existing = pr.labels.map((l) => l.name);
if (categories.some(([label]) => existing.includes(label))) {
core.info('PR is already categorised, nothing to do');
return;
}

// Read the Category field, ignoring the template's comment hints
const body = (pr.body || '').replace(/<!--[\s\S]*?-->/g, '');
const stated = (body.match(/#+\s*Category\s*([^\n#]*)/) || ['', ''])[1].trim();
let labels = categories.filter(([, field]) => field.test(stated)).map(([label]) => label);

// If no valid category was given, infer one from the files changed
if (!labels.length) {
const files = await github.paginate(github.rest.pulls.listFiles, {
...context.repo, pull_number: pr.number, per_page: 100,
});
const names = files.map((f) => f.filename);
const counts = categories.map(([label, , paths]) =>
[label, paths ? names.filter((n) => paths.test(n)).length : 0]);
const [label, hits] = counts.reduce((a, b) => (b[1] > a[1] ? b : a));
if (hits * 2 > names.length) labels = [label];
}

if (!labels.length) {
core.info('Unable to determine a category for this PR');
return;
}
await github.rest.issues.addLabels({ ...context.repo, issue_number: pr.number, labels });
core.info(`Applied label(s): ${labels.join(', ')}`);
25 changes: 23 additions & 2 deletions .github/workflows/tag.yml
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,25 @@ jobs:
const prAuthor = context.payload.pull_request.user?.login;
const creditAuthor = prAuthor && prAuthor.toLowerCase() !== 'lissy93';
const marker = `released-${version}`;

// Public sponsors, so we don't ask them to sponsor us again
const sponsors = new Set();
try {
const query = `query($login: String!, $cursor: String) {
user(login: $login) { sponsors(first: 100, after: $cursor) {
nodes { ... on User { login } ... on Organization { login } }
pageInfo { hasNextPage endCursor }
} }
}`;
let cursor = null;
do {
const { user } = await github.graphql(query, { login: owner, cursor });
user.sponsors.nodes.forEach(n => n?.login && sponsors.add(n.login.toLowerCase()));
cursor = user.sponsors.pageInfo.hasNextPage ? user.sponsors.pageInfo.endCursor : null;
} while (cursor);
} catch (e) {
core.warning(`Failed to fetch sponsors: ${e.message}`);
}
for (const num of issues) {
const issue_number = parseInt(num, 10);
try {
Expand Down Expand Up @@ -294,8 +313,10 @@ jobs:
];
if (isOld) parts.push(`\nWe're sorry this one took so long 😔`);
if (isNew) {
parts.push(`\nIf you're enjoying Dashy, consider ` +
`[sponsoring us](https://github.com/sponsors/lissy93) on GitHub to help with development 💖`);
parts.push(sponsors.has(author?.toLowerCase())
? `\nThank you for supporting Dashy's development with your sponsorship 💖`
: `\nIf you're enjoying Dashy, consider ` +
`[sponsoring us](https://github.com/sponsors/lissy93) on GitHub to help with development 💖`);
}
parts.push(`<!-- ${marker} -->`);
const body = parts.join(' ');
Expand Down