5 Commits
Author SHA1 Message Date
Patrick Niebeling ce749e63a4 Fix overdue check to compare real timestamps, not whole calendar days
Build package / package (push) Successful in 57s
Build package / php-lint (8.4) (push) Successful in 44s
Build package / php-lint (8.5) (push) Successful in 35s
Build package / xml-lint (push) Successful in 11s
Build package / unit-tests (push) Successful in 37s
WorkflowRunner::isOverdue() took Card::getDaysUntilDue(), which Deck
computes by truncating both "now" and the due date to midnight before
diffing. A card due earlier today therefore reported 0 days until due -
not overdue - until the calendar date rolled over to the next day, so
a card due "this morning" sat unmoved for up to 24h while one due
"yesterday morning" moved immediately. isOverdue() now takes the due
date and $now directly and compares them as real timestamps, matching
what Deck's own UI shows.

Bump to 34.0.2.
2026-08-25 13:48:02 +02:00
Patrick Niebeling b203ba35f8 Align declared PHP compatibility with CI: require 8.4-8.5
Build package / php-lint (8.4) (push) Successful in 36s
Build package / php-lint (8.5) (push) Successful in 35s
Build package / xml-lint (push) Successful in 14s
Build package / unit-tests (push) Successful in 37s
Build package / package (push) Successful in 52s
Bump to 34.0.1.
2026-08-25 13:28:52 +02:00
Patrick Niebeling ef973af4b5 Drop PHP 8.2/8.3 from CI, test against 8.4/8.5 only
Build package / php-lint (8.4) (push) Successful in 36s
Build package / php-lint (8.5) (push) Successful in 33s
Build package / xml-lint (push) Successful in 11s
Build package / unit-tests (push) Successful in 39s
Build package / package (push) Successful in 52s
2026-08-25 13:14:02 +02:00
Patrick Niebeling a95d5e2d8a Switch to Nextcloud-major-aligned versioning starting at 34.0.0
Build package / package (push) Successful in 55s
Build package / php-lint (8.2) (push) Successful in 40s
Build package / php-lint (8.3) (push) Successful in 43s
Build package / php-lint (8.4) (push) Successful in 35s
Build package / php-lint (8.5) (push) Successful in 35s
Build package / xml-lint (push) Successful in 11s
Build package / unit-tests (push) Successful in 36s
Leading version number now tracks the supported Nextcloud major
(currently 34, i.e. Hub 26 Spring) instead of an independent 0.x
count, so compatibility is visible at a glance. Documents the scheme
in CLAUDE.md's release process section.
2026-08-25 13:06:47 +02:00
Patrick Niebeling 2d2f3ca19c Fix overdue detection: stop discarding enriched cards for CardDetails wrappers
Build package / php-lint (8.2) (push) Successful in 45s
Build package / php-lint (8.3) (push) Successful in 39s
Build package / php-lint (8.4) (push) Successful in 42s
Build package / php-lint (8.5) (push) Successful in 39s
Build package / xml-lint (push) Successful in 12s
Build package / unit-tests (push) Successful in 44s
Build package / package (push) Successful in 1m1s
getActiveCardsInStack() reassigned $cards to CardService::enrichCards()'s
return value. That return value is an array of CardDetails wrappers whose
own entity fields (duedate, stackId, ...) are never populated from the
wrapped card - only its overridden jsonSerialize() reads through to them.
So every card's getDuedate()/getDaysUntilDue() resolved to null, and
WorkflowRunner::isOverdue() was always false: no workflow could ever match
a card, regardless of its actual due date.

enrichCards() already mutates the original $cards entries in place (labels,
assigned users, comment counts, ...), so the fix is to keep using that
array instead of capturing the CardDetails return value.

Bump to 0.2.3.
2026-08-25 13:01:35 +02:00
7 changed files with 47 additions and 14 deletions
+2 -2
View File
@@ -15,7 +15,7 @@ jobs:
runs-on: gitea-runner-server03
strategy:
matrix:
php-version: ['8.2', '8.3', '8.4', '8.5']
php-version: ['8.4', '8.5']
steps:
- uses: actions/checkout@v7
@@ -51,7 +51,7 @@ jobs:
- uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
php-version: '8.4'
extensions: sqlite, pdo_sqlite
coverage: none
+2
View File
@@ -109,6 +109,8 @@ There is no meaningful way to test the Deck integration itself outside a real Ne
### Release process
**Versioning scheme (from 34.0.0 on): `<nextcloud-major>.<minor>.<patch>`.** The leading number tracks the minimum/maximum Nextcloud major version declared in `appinfo/info.xml`'s `<dependencies><nextcloud .../>` (currently `min-version="34" max-version="34"`), not this app's own feature history - so it only changes when the supported Nextcloud major changes (e.g. dropping/adding NC 34 support bumps to `35.0.0`), and resets `minor`/`patch` to `0.0` at that point. Ordinary fixes/features within the same supported NC major bump `minor`/`patch` as usual (semver-ish, without a leading `0.`). Versions before `34.0.0` (`0.1.x`/`0.2.x`) predate this scheme.
1. Bump `<version>` in `appinfo/info.xml` and `version` in `package.json` (keep them in sync).
2. Commit, push to `main`, confirm the run of `.gitea/workflows/build-main.yml` is green.
3. `git tag vX.Y.Z && git push origin vX.Y.Z` — this triggers the tag branch of the same workflow, which re-runs the checks, builds the frontend, runs `make appstore`, and attaches `workflow_deck_automation.tar.gz` as the asset on a normal (non-pre-) release for that tag.
+2 -2
View File
@@ -28,7 +28,7 @@ This app lets every user configure their own Deck automation workflows from Pers
A background job periodically evaluates all enabled workflows and moves cards whose due date has passed, using Deck's own internal PHP classes only (no HTTP/OCS calls).
]]></description>
<version>0.2.2</version>
<version>34.0.2</version>
<licence>agpl</licence>
<author mail="patrick@niebel.ing">Patrick Niebeling</author>
<namespace>WorkflowDeckAutomation</namespace>
@@ -36,7 +36,7 @@ A background job periodically evaluates all enabled workflows and moves cards wh
<category>tools</category>
<bugs>https://github.com/pniebeling/workflow_deck_automation/issues</bugs>
<dependencies>
<php min-version="8.2" max-version="8.5"/>
<php min-version="8.4" max-version="8.5"/>
<database min-version="9.4">pgsql</database>
<database>sqlite</database>
<database min-version="8.0">mysql</database>
+7 -1
View File
@@ -483,7 +483,13 @@ class DeckIntegrationService {
$cardService = $this->resolve(CardService::class);
$cards = $cardMapper->findAllByStack($stackId);
$cards = $cardService->enrichCards($cards);
// enrichCards() mutates each $cards entry in place (labels, assigned users, ...) but
// *returns* CardDetails wrappers whose own fields (duedate, stackId, ...) were never
// copied from the wrapped card - only its overridden jsonSerialize() reads through to
// them. Capturing that return value here made every card's getDuedate()/getDaysUntilDue()
// resolve to null, so isOverdue() was always false and no workflow ever matched a card.
// Keep using the original, now-enriched $cards instead.
$cardService->enrichCards($cards);
return array_values(array_filter($cards, static function (Card $card) {
if (method_exists($card, 'getDeletedAt') && (int)$card->getDeletedAt() > 0) {
+15 -3
View File
@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace OCA\WorkflowDeckAutomation\Service;
use DateTimeInterface;
use OCA\Deck\Db\Card;
use OCA\WorkflowDeckAutomation\Db\Workflow;
use OCA\WorkflowDeckAutomation\Db\WorkflowMapper;
@@ -165,8 +166,10 @@ class WorkflowRunner {
// and the workflow wants a mail — this is decoration, not a check.
$targets = null;
$now = $this->timeFactory->getDateTime();
foreach ($cards as $card) {
if (!self::isOverdue($card->getDaysUntilDue())) {
if (!self::isOverdue($card->getDuedate(), $now)) {
continue;
}
@@ -319,8 +322,17 @@ class WorkflowRunner {
}
}
public static function isOverdue(?int $daysUntilDue): bool {
return $daysUntilDue !== null && $daysUntilDue < 0;
/**
* Compares the card's actual due timestamp against $now instead of Deck's own
* Card::getDaysUntilDue(), which truncates both sides to midnight before diffing
* (DateInterval's %a is a whole-day count) and therefore reports 0 - not overdue -
* for any card whose due time has already passed today. That silently held back
* every card for up to 24h after it actually fell due; comparing the raw datetime
* instead matches what Deck's own UI shows (e.g. "vor 2 Stunden") and has no
* day-rounding step to get wrong.
*/
public static function isOverdue(?DateTimeInterface $duedate, DateTimeInterface $now): bool {
return $duedate !== null && $duedate < $now;
}
/**
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "workflow_deck_automation",
"version": "0.2.2",
"version": "34.0.2",
"private": true,
"type": "module",
"description": "Automates moving overdue Deck cards between stacks, configurable per user.",
@@ -4,22 +4,35 @@ declare(strict_types=1);
namespace OCA\WorkflowDeckAutomation\Tests\Unit\Service;
use DateTimeImmutable;
use OCA\WorkflowDeckAutomation\Service\WorkflowRunner;
use PHPUnit\Framework\TestCase;
class WorkflowRunnerFilterTest extends TestCase {
public function testIsOverdueReturnsFalseWithoutDueDate(): void {
$this->assertFalse(WorkflowRunner::isOverdue(null));
$this->assertFalse(WorkflowRunner::isOverdue(null, new DateTimeImmutable('2026-01-15 12:00:00')));
}
public function testIsOverdueReturnsFalseForFutureDueDate(): void {
$this->assertFalse(WorkflowRunner::isOverdue(0));
$this->assertFalse(WorkflowRunner::isOverdue(3));
$now = new DateTimeImmutable('2026-01-15 12:00:00');
$this->assertFalse(WorkflowRunner::isOverdue($now->modify('+1 hour'), $now));
$this->assertFalse(WorkflowRunner::isOverdue($now->modify('+3 days'), $now));
}
public function testIsOverdueReturnsTrueForPastDueDate(): void {
$this->assertTrue(WorkflowRunner::isOverdue(-1));
$this->assertTrue(WorkflowRunner::isOverdue(-30));
$now = new DateTimeImmutable('2026-01-15 12:00:00');
$this->assertTrue(WorkflowRunner::isOverdue($now->modify('-1 hour'), $now));
$this->assertTrue(WorkflowRunner::isOverdue($now->modify('-30 days'), $now));
}
public function testIsOverdueReturnsTrueForADueTimeEarlierTheSameDay(): void {
// Regression: comparing whole calendar days (as Deck's own
// Card::getDaysUntilDue() does) reports a card due earlier today as
// "0 days until due", not overdue, until the calendar date rolls
// over - holding every card back for up to 24h after it actually
// fell due. isOverdue() must compare the real timestamps instead.
$now = new DateTimeImmutable('2026-01-15 12:00:00');
$this->assertTrue(WorkflowRunner::isOverdue(new DateTimeImmutable('2026-01-15 06:00:00'), $now));
}
public function testNoFiltersMatchesAnyCard(): void {