Skip to content

Commit 53bdb65

Browse files
authored
Merge branch 'main' into fix-for-issue-8888
2 parents 2a244a3 + db1e651 commit 53bdb65

32 files changed

+1177
-214
lines changed

.github/ghprcomment.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@
170170
# PR hygiene
171171

172172
- jobName: 'Ensure that contributor is assigned (fails if not commented on issue)'
173-
workflowName: 'On PR opened/updated'
173+
workflowName: 'Link PR to Issue'
174174
message: >
175175
You did not assign yourself to the issue.
176176
Thus, it looks like you skipped reading our [CONTRIBUTING.md](https://github.com/JabRef/jabref/blob/main/CONTRIBUTING.md), which explains exactly how to participate. No worries, it happens to the best of us.
@@ -196,7 +196,7 @@
196196
message: >
197197
Note that your PR will not be reviewed/accepted until you have gone through the mandatory checks in the description and marked each of them them exactly in the format of `[x]` (done), `[ ]` (not done yet) or `[/]` (not applicable).
198198
- jobName: 'Determine issue number'
199-
workflowName: 'On PR opened/updated'
199+
workflowName: 'Link PR to Issue'
200200
always: true
201201
message: |
202202
Your pull request needs to link an issue correctly.

.github/workflows/link-issue.yml

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
name: Link PR to Issue
2+
3+
on:
4+
pull_request_target:
5+
types: [opened, reopened, edited]
6+
7+
jobs:
8+
determine_issue_number:
9+
name: Determine issue number
10+
runs-on: ubuntu-latest
11+
if: >
12+
(github.repository == 'JabRef/jabref') &&
13+
(github.event.pull_request.head.repo.full_name != 'JabRef/jabref') &&
14+
!(
15+
(github.event.pull_request.user.login == 'dependabot[bot]') || (github.event.pull_request.user.login == 'renovate-bot') ||
16+
(
17+
startsWith(github.event.pull_request.title, '[Bot] ') ||
18+
startsWith(github.event.pull_request.title, 'Bump ') ||
19+
startsWith(github.event.pull_request.title, 'New Crowdin updates') ||
20+
startsWith(github.event.pull_request.title, 'Update Gradle Wrapper from')
21+
)
22+
)
23+
permissions:
24+
contents: read
25+
outputs:
26+
issue_number: ${{ steps.get_issue_number.outputs.ticketNumber }}
27+
steps:
28+
- name: echo PR data
29+
run: |
30+
echo "PR Number: ${{ github.event.pull_request.number }}"
31+
echo "PR URL: ${{ github.event.pull_request.html_url }}"
32+
cat <<EOF
33+
PR Body:
34+
${{ github.event.pull_request.body }}
35+
EOF
36+
- name: Determine issue number
37+
id: get_issue_number
38+
uses: koppor/ticket-check-action@add-output
39+
with:
40+
token: ${{ secrets.GITHUB_TOKEN }}
41+
ticketLink: 'https://github.com/JabRef/jabref/issues/%ticketNumber%'
42+
ticketPrefix: '#'
43+
titleRegex: '^#(?<ticketNumber>\d+)'
44+
branchRegex: '^(?<ticketNumber>\d+)'
45+
# Matches GitHub's closes/fixes/resolves #{number}, but does not match our example `Closes #13109` in PULL_REQUEST_TEMPLATE
46+
# Also matches URLs that are wrapped in `<>`.
47+
bodyRegex: '(?<action>fixes|closes|resolves|refs)\s+<?(?:https?:\/\/github\.com\/JabRef\/jabref\/issues\/)?#?(?<ticketNumber>(?!13109\b)\d+)>?'
48+
bodyRegexFlags: 'i'
49+
outputOnly: true
50+
- run: echo "${{ steps.get_issue_number.outputs.ticketNumber }}"
51+
- name: Issue number present
52+
if: steps.get_issue_number.outputs.ticketNumber == '-1'
53+
run: |
54+
echo "No valid ticket number found!"
55+
exit 1
56+
57+
move_issue:
58+
name: Mark issue as in progress
59+
# after determine_issue_number to ensure that there is only one failure because of no ticket number
60+
needs: determine_issue_number
61+
runs-on: ubuntu-latest
62+
permissions:
63+
issues: write
64+
steps:
65+
- name: Move issue to "In Progress" in "Good First Issues"
66+
uses: m7kvqbe1/github-action-move-issues/@main
67+
with:
68+
github-token: ${{ secrets.GH_TOKEN_ACTION_MOVE_ISSUE }}
69+
project-url: "https://github.com/orgs/JabRef/projects/5"
70+
target-labels: "📍 Assigned"
71+
target-column: "In Progress"
72+
ignored-columns: ""
73+
default-column: "In Progress"
74+
issue-number: ${{ needs.determine_issue_number.outputs.issue_number }}
75+
skip-if-not-in-project: true
76+
- name: Move issue to "In Progress" in "Candidates for University Projects"
77+
uses: m7kvqbe1/github-action-move-issues/@main
78+
with:
79+
github-token: ${{ secrets.GH_TOKEN_ACTION_MOVE_ISSUE }}
80+
project-url: "https://github.com/orgs/JabRef/projects/3"
81+
target-labels: "📍 Assigned"
82+
target-column: "In Progress"
83+
ignored-columns: ""
84+
default-column: "In Progress"
85+
issue-number: ${{ needs.determine_issue_number.outputs.issue_number }}
86+
skip-if-not-in-project: true
87+
88+
ensure_assignment:
89+
name: Ensure that contributor is assigned (fails if not commented on issue)
90+
if: github.event.pull_request.head.repo.full_name != 'JabRef/jabref'
91+
# after determine_issue_number to ensure that there is only one failure because of no ticket number
92+
needs: determine_issue_number
93+
runs-on: ubuntu-latest
94+
permissions:
95+
issues: write
96+
steps:
97+
- uses: actions/checkout@v5
98+
with:
99+
show-progress: 'false'
100+
- name: Assign PR creator to issue
101+
run: |
102+
set -e
103+
104+
echo "Updating issue '${{ needs.determine_issue_number.outputs.issue_number }}'"
105+
106+
# "gh issue edit" cannot be used - workaround found at https://github.com/cli/cli/issues/9620#issuecomment-2703135049
107+
108+
ASSIGNEES=$(gh api /repos/JabRef/jabref/issues/${{ needs.determine_issue_number.outputs.issue_number }} --jq '[.assignees[].login]')
109+
110+
# Check if the user is already assigned
111+
if echo "$ASSIGNEES" | jq -e '. | index("${{ github.event.pull_request.user.login }}")' >/dev/null; then
112+
echo "User '${{ github.event.pull_request.user.login }}' is already an assignee. No update needed."
113+
echo "Debug: $ASSIGNEES"
114+
exit 0
115+
fi
116+
117+
# Append the new assignee
118+
UPDATED_ASSIGNEES=$(echo "$ASSIGNEES" | jq --arg new "${{ github.event.pull_request.user.login }}" '. + [$new]')
119+
120+
LABELS=$(gh api repos/${{ github.repository }}/issues/${{ needs.determine_issue_number.outputs.issue_number }}/labels --jq '.[].name')
121+
LABEL=$(echo "$LABELS" | grep -E '^good (first|second|third|forth) issue$' || true)
122+
if [ -n "$LABEL" ]; then
123+
echo "✅ Found label: $LABEL"
124+
SILENT=false
125+
# Apply label
126+
gh issue edit "${{ github.event.pull_request.number }}" --add-label "$LABEL"
127+
else
128+
echo "🚫 Silent fail if not possible to add assignee"
129+
SILENT=true
130+
fi
131+
132+
# Update issue with the new assignee list
133+
echo "Updating issue #${{ needs.determine_issue_number.outputs.issue_number }} updated with assignees: $UPDATED_ASSIGNEES..."
134+
if [ "$SILENT" = true ]; then
135+
gh api -X PATCH /repos/JabRef/jabref/issues/${{ needs.determine_issue_number.outputs.issue_number }} --input <(echo "{\"assignees\": $UPDATED_ASSIGNEES}") || true
136+
else
137+
gh api -X PATCH /repos/JabRef/jabref/issues/${{ needs.determine_issue_number.outputs.issue_number }} --input <(echo "{\"assignees\": $UPDATED_ASSIGNEES}")
138+
fi
139+
env:
140+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
141+
- name: Add label "📌 Pinned"
142+
run: gh issue edit ${{ needs.determine_issue_number.outputs.issue_number }} --add-label "📌 Pinned"
143+
env:
144+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.github/workflows/on-pr-opened-updated.yml

Lines changed: 0 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -6,141 +6,6 @@ on:
66
# default: opened, synchronize, reopened
77

88
jobs:
9-
determine_issue_number:
10-
name: Determine issue number
11-
runs-on: ubuntu-latest
12-
if: >
13-
(github.repository == 'JabRef/jabref') &&
14-
(github.event.pull_request.head.repo.full_name != 'JabRef/jabref') &&
15-
!(
16-
(github.event.pull_request.user.login == 'dependabot[bot]') || (github.event.pull_request.user.login == 'renovate-bot') ||
17-
(
18-
startsWith(github.event.pull_request.title, '[Bot] ') ||
19-
startsWith(github.event.pull_request.title, 'Bump ') ||
20-
startsWith(github.event.pull_request.title, 'New Crowdin updates') ||
21-
startsWith(github.event.pull_request.title, 'Update Gradle Wrapper from')
22-
)
23-
)
24-
permissions:
25-
contents: read
26-
outputs:
27-
issue_number: ${{ steps.get_issue_number.outputs.ticketNumber }}
28-
steps:
29-
- name: echo PR data
30-
run: |
31-
echo "PR Number: ${{ github.event.pull_request.number }}"
32-
echo "PR URL: ${{ github.event.pull_request.html_url }}"
33-
cat <<EOF
34-
PR Body:
35-
${{ github.event.pull_request.body }}
36-
EOF
37-
- name: Determine issue number
38-
id: get_issue_number
39-
uses: koppor/ticket-check-action@add-output
40-
with:
41-
token: ${{ secrets.GITHUB_TOKEN }}
42-
ticketLink: 'https://github.com/JabRef/jabref/issues/%ticketNumber%'
43-
ticketPrefix: '#'
44-
titleRegex: '^#(?<ticketNumber>\d+)'
45-
branchRegex: '^(?<ticketNumber>\d+)'
46-
# Matches GitHub's closes/fixes/resolves #{number}, but does not match our example `Closes #13109` in PULL_REQUEST_TEMPLATE
47-
# Also matches URLs that are wrapped in `<>`.
48-
bodyRegex: '(?<action>fixes|closes|resolves|refs)\s+<?(?:https?:\/\/github\.com\/JabRef\/jabref\/issues\/)?#?(?<ticketNumber>(?!13109\b)\d+)>?'
49-
bodyRegexFlags: 'i'
50-
outputOnly: true
51-
- run: echo "${{ steps.get_issue_number.outputs.ticketNumber }}"
52-
- name: Issue number present
53-
if: steps.get_issue_number.outputs.ticketNumber == '-1'
54-
run: |
55-
echo "No valid ticket number found!"
56-
exit 1
57-
move_issue:
58-
name: Mark issue as in progress
59-
# after determine_issue_number to ensure that there is only one failure because of no ticket number
60-
needs: determine_issue_number
61-
runs-on: ubuntu-latest
62-
permissions:
63-
issues: write
64-
steps:
65-
- name: Move issue to "In Progress" in "Good First Issues"
66-
uses: m7kvqbe1/github-action-move-issues/@main
67-
with:
68-
github-token: ${{ secrets.GH_TOKEN_ACTION_MOVE_ISSUE }}
69-
project-url: "https://github.com/orgs/JabRef/projects/5"
70-
target-labels: "📍 Assigned"
71-
target-column: "In Progress"
72-
ignored-columns: ""
73-
default-column: "In Progress"
74-
issue-number: ${{ needs.determine_issue_number.outputs.issue_number }}
75-
skip-if-not-in-project: true
76-
- name: Move issue to "In Progress" in "Candidates for University Projects"
77-
uses: m7kvqbe1/github-action-move-issues/@main
78-
with:
79-
github-token: ${{ secrets.GH_TOKEN_ACTION_MOVE_ISSUE }}
80-
project-url: "https://github.com/orgs/JabRef/projects/3"
81-
target-labels: "📍 Assigned"
82-
target-column: "In Progress"
83-
ignored-columns: ""
84-
default-column: "In Progress"
85-
issue-number: ${{ needs.determine_issue_number.outputs.issue_number }}
86-
skip-if-not-in-project: true
87-
ensure_assignment:
88-
name: Ensure that contributor is assigned (fails if not commented on issue)
89-
if: github.event.pull_request.head.repo.full_name != 'JabRef/jabref'
90-
# after determine_issue_number to ensure that there is only one failure because of no ticket number
91-
needs: determine_issue_number
92-
runs-on: ubuntu-latest
93-
permissions:
94-
issues: write
95-
steps:
96-
- uses: actions/checkout@v5
97-
with:
98-
show-progress: 'false'
99-
- name: Assign PR creator to issue
100-
run: |
101-
set -e
102-
103-
echo "Updating issue '${{ needs.determine_issue_number.outputs.issue_number }}'"
104-
105-
# "gh issue edit" cannot be used - workaround found at https://github.com/cli/cli/issues/9620#issuecomment-2703135049
106-
107-
ASSIGNEES=$(gh api /repos/JabRef/jabref/issues/${{ needs.determine_issue_number.outputs.issue_number }} --jq '[.assignees[].login]')
108-
109-
# Check if the user is already assigned
110-
if echo "$ASSIGNEES" | jq -e '. | index("${{ github.event.pull_request.user.login }}")' >/dev/null; then
111-
echo "User '${{ github.event.pull_request.user.login }}' is already an assignee. No update needed."
112-
echo "Debug: $ASSIGNEES"
113-
exit 0
114-
fi
115-
116-
# Append the new assignee
117-
UPDATED_ASSIGNEES=$(echo "$ASSIGNEES" | jq --arg new "${{ github.event.pull_request.user.login }}" '. + [$new]')
118-
119-
LABELS=$(gh api repos/${{ github.repository }}/issues/${{ needs.determine_issue_number.outputs.issue_number }}/labels --jq '.[].name')
120-
LABEL=$(echo "$LABELS" | grep -E '^good (first|second|third|forth) issue$' || true)
121-
if [ -n "$LABEL" ]; then
122-
echo "✅ Found label: $LABEL"
123-
SILENT=false
124-
# Apply label
125-
gh issue edit "${{ github.event.pull_request.number }}" --add-label "$LABEL"
126-
else
127-
echo "🚫 Silent fail if not possible to add assignee"
128-
SILENT=true
129-
fi
130-
131-
# Update issue with the new assignee list
132-
echo "Updating issue #${{ needs.determine_issue_number.outputs.issue_number }} updated with assignees: $UPDATED_ASSIGNEES..."
133-
if [ "$SILENT" = true ]; then
134-
gh api -X PATCH /repos/JabRef/jabref/issues/${{ needs.determine_issue_number.outputs.issue_number }} --input <(echo "{\"assignees\": $UPDATED_ASSIGNEES}") || true
135-
else
136-
gh api -X PATCH /repos/JabRef/jabref/issues/${{ needs.determine_issue_number.outputs.issue_number }} --input <(echo "{\"assignees\": $UPDATED_ASSIGNEES}")
137-
fi
138-
env:
139-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
140-
- name: Add label "📌 Pinned"
141-
run: gh issue edit ${{ needs.determine_issue_number.outputs.issue_number }} --add-label "📌 Pinned"
142-
env:
143-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
1449
conflicts_with_target:
14510
if: github.repository == 'JabRef/jabref'
14611
name: Conflicts with target branch

.github/workflows/pr-comment.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ name: Comment on PR
88
on:
99
workflow_run:
1010
# note when updating via a PR and testing - `workflow_run` executes from the `main` branch and not the PR branch
11-
workflows: ["Source Code Tests", "On PR opened/updated", "Check PR Format", "Check PR Modifications", "Check PR CHANGELOG.md"]
11+
workflows: ["Source Code Tests", "On PR opened/updated", "Check PR Format", "Link PR to Issue", "Check PR Modifications", "Check PR CHANGELOG.md"]
1212
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#running-a-workflow-based-on-the-conclusion-of-another-workflow
1313
types: [completed]
1414
workflow_dispatch:

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv
152152
- We prevented a brief flash of the default JavaFX (Modena) theme on startup. [#13877](https://github.com/JabRef/jabref/pull/13877)
153153
- We fixed an issue where button-bar buttons truncated long text with ellipsis. [#13877](https://github.com/JabRef/jabref/pull/13877)
154154
- We fixed an issue where ignoring of subdirectories via `.gitingore` patterns did not work in the "Find unlinked files dialog". [forum#5425](https://discourse.jabref.org/t/set-list-of-ignored-folders-paths/5425/6)
155+
- We fixed an issue where CTRL+W does not close the current tab [#12530](https://github.com/JabRef/jabref/issues/12530)
155156
- We fixed an issue where the "Applications to push entries to" list in the preferences was not sorted alphabetically. [#14058](https://github.com/JabRef/jabref/issues/14058)
156157
- We fixed an issue where notice text in AI chat was not automatically refreshed when the user changed preferences.[#13855](https://github.com/JabRef/jabref/issues/13855)
157158
- We fixed an issue where the user could add custom entry types with spaces in their names. [#14088](https://github.com/JabRef/jabref/issues/14088)

build-logic/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ dependencies {
1111
implementation("com.autonomousapps:dependency-analysis-gradle-plugin:3.1.0")
1212
implementation("com.github.andygoossens:gradle-modernizer-plugin:1.12.0")
1313
implementation("org.gradlex:extra-java-module-info:1.13.1")
14-
implementation("org.gradlex:java-module-dependencies:1.10")
14+
implementation("org.gradlex:java-module-dependencies:1.11")
1515
implementation("org.gradlex:java-module-packaging:1.1") // required for platform-specific packaging of JavaFX dependencies
1616
implementation("org.gradlex:java-module-testing:1.7")
1717
implementation("org.gradlex:jvm-dependency-conflict-resolution:2.4")

docs/decisions/0039-use-apache-velocity-as-template-engine.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ Chosen option: "Apache Velocity", because "Velocity's goal is to keep templates
3636
Furthermore, Apache Velocity is lightweight, and it allows to generate text output. This is a good fit for the AI feature.
3737

3838
Update from 01.10.2025: more promising options were added (Handlebars and Jinja), but the final decision was not discussed and updated.
39+
Update from 20.10.2025: added pebble
3940

4041
## Pros and Cons of the Options
4142

@@ -137,6 +138,7 @@ Here are the papers you are analyzing:
137138
* Good, because it is powerful and flexible.
138139
* Good, because it has a simple API.
139140
* Neutral, as custom functions needs to be added manually. You cannot pass an ordinary Java object and use it as you want.
141+
* Bad, because as a Java port it lacks behind mainline development.
140142

141143
### Jinja
142144

@@ -161,6 +163,31 @@ Here are the papers you are analyzing:
161163
* Neutral, as it was developed for web and Python, not for Java.
162164
* Neutral, as the Java port is quite young (in comparison to other options).
163165
* Neutral, as custom functions needs to be added manually. You cannot pass an ordinary Java object and use it as you want.
166+
* Bad, because as a Java port it lacks behind mainline development.
167+
168+
### Pebble
169+
170+
- Main page: <https://pebbletemplates.io/>
171+
- Repository and developer guide: <https://github.com/PebbleTemplates/pebble>
172+
- User guide: <https://pebbletemplates.io/wiki/>
173+
174+
```text
175+
{% for entry in entries %}
176+
{{ entry.title }}
177+
{{ entry.author }}
178+
{% else %}
179+
There are no entries.
180+
{% endfor %}
181+
```
182+
183+
* Good, because supports plain text templating.
184+
* Good, because it is possible to use `String` as a template.
185+
* Good, because it supports template inheritance, includes, and custom functions (`macros`).
186+
* Good, because it is actively maintained.
187+
* Good, because it provides a simple API that integrates easily with Java.
188+
* Good, because has tooling support for common IDEs.
189+
* Neutral, because its feature set is smaller than FreeMarker’s, but sufficient for text-based generation.
190+
* Neutral, because it is less widely adopted than Thymeleaf or FreeMarker.
164191

165192
## More Information
166193

jabgui/src/main/java/org/jabref/gui/actions/StandardActions.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ public enum StandardActions implements Action {
8686
EXPORT_SELECTED(Localization.lang("Export selected entries"), KeyBinding.EXPORT_SELECTED),
8787
CONNECT_TO_SHARED_DB(Localization.lang("Connect to shared database"), IconTheme.JabRefIcons.CONNECT_DB),
8888
PULL_CHANGES_FROM_SHARED_DB(Localization.lang("Pull changes from shared database"), KeyBinding.PULL_CHANGES_FROM_SHARED_DATABASE),
89-
CLOSE_LIBRARY(Localization.lang("Close library"), Localization.lang("Close the current library"), IconTheme.JabRefIcons.CLOSE, KeyBinding.CLOSE_DATABASE),
89+
CLOSE_LIBRARY(Localization.lang("Close library"), Localization.lang("Close the current library"), IconTheme.JabRefIcons.CLOSE),
9090
CLOSE_OTHER_LIBRARIES(Localization.lang("Close others"), Localization.lang("Close other libraries"), IconTheme.JabRefIcons.CLOSE),
9191
CLOSE_ALL_LIBRARIES(Localization.lang("Close all"), Localization.lang("Close all libraries"), IconTheme.JabRefIcons.CLOSE),
9292
QUIT(Localization.lang("Quit"), Localization.lang("Quit JabRef"), IconTheme.JabRefIcons.CLOSE_JABREF, KeyBinding.QUIT_JABREF),

jabgui/src/main/java/org/jabref/gui/frame/JabRefFrame.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,10 @@ private void initKeyBindings() {
379379
Optional.ofNullable(getCurrentLibraryTab()).ifPresent(LibraryTab::forward);
380380
event.consume();
381381
break;
382+
case CLOSE_DATABASE:
383+
new CloseDatabaseAction(this, stateManager).execute();
384+
event.consume();
385+
break;
382386
default:
383387
}
384388
}

0 commit comments

Comments
 (0)