Skip to content

chore(quality): clear phpstan baseline#285

Merged
CybotTM merged 8 commits into
mainfrom
chore/phpstan-baseline-cleanup
May 8, 2026
Merged

chore(quality): clear phpstan baseline#285
CybotTM merged 8 commits into
mainfrom
chore/phpstan-baseline-cleanup

Conversation

@CybotTM
Copy link
Copy Markdown
Member

@CybotTM CybotTM commented May 6, 2026

Summary

Drives phpstan-baseline.neon from 80 errors / 40 entries down to 0 by fixing the underlying issues — no new ignore patterns, no level changes, no path exclusions.

Patterns fixed

Group Where Approach
implode / natcasesort over list<mixed> Service/Integration/Jira/JiraAuthenticationService, JiraHttpClientService, JiraOAuthApiService, Service/SubticketSyncService, Command/TtSyncSubticketsCommand Tightened return types on getSubtickets/syncProjectSubtickets to list<string>; recursive stringifyParsedValue() helper for parse_str output; array_map(strval-equivalent) over decoded JSON arrays.
nullCoalesce.property on non-nullable $filepath Traits/TestDataTrait Dropped the redundant ?? null.
Decoded JSON returning mixed (offsetAccess, method.nonObject, str_contains arg) EventSubscriber/ExceptionSubscriberTest, Service/Integration/Jira/JiraTicketServiceTest Added decodeJsonResponse() helper that asserts JsonResponse + string body and returns array<string, mixed>; in-line array shapes for callback payloads.
staticMethod.dynamicCall for Assert::callback() ExceptionSubscriberTest, JiraTicketServiceTest Switched to self::callback().
data-provider return type mismatch ExceptionSubscriberTest::provideDefaultMessages Relabelled keys so they're non-numeric strings, matching the array<string, …> shape.
array_sum over list<mixed> Performance/PerformanceBenchmarkRunner Coerced array_column() output to float with a numeric guard.
warmUp() argument type and assertContains against mixed Service/Cache/QueryCacheServiceTest + production QueryCacheService::getStats() Tightened getStats() to an explicit array shape; built the deliberately-malformed callbacks map through a mixed intermediate so the call site type-checks.
binaryOp.invalid for 'encrypted_' . $token JiraAuthenticationServiceTest Annotated the willReturnCallback lambdas with string $token.
assertTrue(true) placeholders, unreachable $this->fail() after never JiraAuthenticationServiceTest Replaced placeholders with assertions on actual side effects; restructured the never-return test to return from the catch.
assertInstanceOf(Client::class, $client) always-true, mixed property reads JiraHttpClientServiceTest Removed the redundant assertions; cast results to stdClass before reading properties; annotated the data-provider expectedExceptionClass as class-string<Throwable>.
Production tightening JiraTicketService::createTicket(): object Reflects the existing is_object() guard.

Numbers

  • Baseline before: 40 entries, 80 errors
  • Baseline after: 0 entries, 0 errors

Test plan

  • phpstan analyze --no-progress → No errors
  • phpstan analyze --generate-baseline → would emit empty baseline
  • PHPUnit on every touched test file (Jira{Authentication,Http,Ticket}ServiceTest, ExceptionSubscriberTest, QueryCacheServiceTest) → all pass
  • Full make test → identical pre-existing failures only (LDAP integration tests need the dev profile up; EntryRepositoryExportTest exception-handler issue; ExportActionPerformanceTest flake noted in the task brief). Verified by re-running on main with my changes stashed.
  • php-cs-fixer fix --dry-run --diff → clean after applying the one suggestion (Throwable import).

CybotTM added 7 commits May 6, 2026 14:19
PHPStan flagged several call sites where implode() or natcasesort() received
arrays of mixed values produced by parse_str() or json_decode(). The code
assumed scalar/string values, which is what these APIs typically yield, but
the static type system could not prove it.

- JiraAuthenticationService::extractTokens() now funnels parsed values
  through a small recursive helper that coerces scalars to strings and drops
  non-stringable values, eliminating the implode mismatches without changing
  behaviour for well-formed responses.
- JiraHttpClientService::extractErrorMessage() coerces decoded JSON values
  with array_map(strval-equivalent) before joining.
- JiraOAuthApiService::getSubtickets() returns list<string> (subtaskKeys is
  already typed as such).
- SubticketSyncService::syncProjectSubtickets() narrows its return type to
  list<string>, which lets natcasesort() and implode() type-check cleanly
  in the command.

Removes 4 baseline entries (5 errors) without altering runtime semantics.

Signed-off-by: Sebastian Mendel <github@sebastianmendel.de>
Replaces dynamic calls to PHPUnit\Framework\Assert::callback() with the
static form, narrows post() callback payloads via inline array shapes, and
asserts that result objects are stdClass before reading properties.

Also tightens JiraTicketService::createTicket() to declare an object return,
matching the existing is_object() guard, and drops the redundant `?? null`
fallback on TestDataTrait::resolveTestDataPath() (the trait's $filepath
property is non-nullable string).

Removes 12 baseline entries (24 errors).

Signed-off-by: Sebastian Mendel <github@sebastianmendel.de>
Adds decodeJsonResponse() helper that asserts the event response is a
JsonResponse with a string body and decodes it into an array<string, mixed>.
Replaces the json_decode((string) $response->getContent(), true) one-liners
that produced mixed values, eliminating the offsetAccess and method.nonObject
errors PHPStan flagged.

Switches the three logger callback expectations to self::callback() and
relabels provideDefaultMessages() keys to non-numeric strings so the
data provider matches its declared @return shape.

Removes 10 baseline entries (27 errors).

Signed-off-by: Sebastian Mendel <github@sebastianmendel.de>
- QueryCacheService::getStats() declares an explicit array shape so tests
  can iterate the returned tags without mixed-typed offset access.
- PerformanceBenchmarkRunner coerces the values pulled out of array_column
  to floats with a numeric guard, fixing the array_sum mismatch.
- QueryCacheServiceTest::warmUpSkipsNonCallableEntries() builds the
  intentionally-malformed callbacks map through a mixed-typed intermediate
  variable so the call site continues to type-check.

Removes 4 baseline entries (8 errors).

Signed-off-by: Sebastian Mendel <github@sebastianmendel.de>
… tests

- Annotate the encryptToken willReturnCallback closures with `string` so
  the binary concat with 'encrypted_' is type-safe.
- Replace `assertTrue(true)` placeholders with assertions on the entity
  state actually exercised by the call, or rely on existing
  expects()->method() expectations as the assertion source.
- Restructure the throwUnauthorizedRedirect originalException test to
  return from the catch block so the unreachable $this->fail() after a
  `never`-returning call disappears.

Removes 3 baseline entries (6 errors).

Signed-off-by: Sebastian Mendel <github@sebastianmendel.de>
…result types

- getClientWith*Mode tests: the method already returns Client, so the
  tail-end assertInstanceOf was always-true. Replace with a comment
  pointing at the existing expects() expectations as the real assertion.
- Cast successful POST/GET/PUT decoded results to stdClass before reading
  properties so the property accesses type-check at level 10.
- Annotate handlesDifferentStatusCodes() expectedExceptionClass parameter
  as class-string<Throwable> to satisfy expectException()'s contract.

Removes the final 5 baseline entries (8 errors). Baseline now empty.

Signed-off-by: Sebastian Mendel <github@sebastianmendel.de>
php-cs-fixer prefers fully-imported references over fully-qualified names
in @param tags.

Signed-off-by: Sebastian Mendel <github@sebastianmendel.de>
Copilot AI review requested due to automatic review settings May 6, 2026 13:23
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request focuses on resolving a large number of static analysis issues by improving type safety across the codebase. Key changes include refining PHPDoc return types, implementing recursive string coercion for parsed OAuth tokens, and adding runtime type checks (such as is_scalar and is_numeric) before operations like implode and array_sum. The test suite was also refactored to include better type assertions and helper methods for decoding JSON responses. Feedback was provided regarding a change in exception testing in JiraAuthenticationServiceTest that could potentially mask regressions; a more robust assertion pattern was suggested to maintain test integrity while satisfying static analysis.

Comment thread tests/Service/Integration/Jira/JiraAuthenticationServiceTest.php
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR removes the need for a PHPStan baseline by fixing the underlying type issues across Jira integration services and related tests, aiming to bring phpstan-baseline.neon to zero ignored errors without changing PHPStan level or exclusions.

Changes:

  • Tightened production return types / phpdoc array shapes (e.g., Jira subticket APIs, QueryCacheService::getStats(), JiraTicketService::createTicket(): object).
  • Refactored tests to satisfy stricter static analysis (typed callbacks, safer JSON decoding helper, stronger result type assertions).
  • Reduced baseline to “empty” (but see comment re: NEON validity).

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/Traits/TestDataTrait.php Removes redundant null-coalesce logic around $filepath resolution.
tests/Service/Integration/Jira/JiraTicketServiceTest.php Adds typed callbacks/array-shapes and asserts returned Jira responses are stdClass.
tests/Service/Integration/Jira/JiraHttpClientServiceTest.php Removes redundant always-true assertions; tightens exception-class typing via phpdoc.
tests/Service/Integration/Jira/JiraAuthenticationServiceTest.php Replaces placeholder assertions and tightens callback parameter typing.
tests/Service/Cache/QueryCacheServiceTest.php Adjusts callback map typing to exercise runtime guards while satisfying PHPStan.
tests/Performance/PerformanceBenchmarkRunner.php Coerces benchmark metrics to numeric floats before aggregation.
tests/EventSubscriber/ExceptionSubscriberTest.php Adds decodeJsonResponse() helper to assert response/body structure and reduce mixed usage.
src/Service/SubticketSyncService.php Tightens documented return type for synced subticket keys.
src/Service/Integration/Jira/JiraTicketService.php Makes createTicket() explicitly return object consistent with existing runtime guard.
src/Service/Integration/Jira/JiraOAuthApiService.php Tightens documented return type for getSubtickets() to list<string>.
src/Service/Integration/Jira/JiraHttpClientService.php Ensures error message extraction builds string lists safely from mixed JSON payloads.
src/Service/Integration/Jira/JiraAuthenticationService.php Adds recursive parse_str() value stringification to avoid mixed array-to-string issues.
src/Service/Cache/QueryCacheService.php Documents getStats() with an explicit array shape.
phpstan-baseline.neon Baseline reduced to empty content (but currently not a valid empty ignoreErrors list).

Comment thread phpstan-baseline.neon Outdated
Comment thread tests/Service/Integration/Jira/JiraAuthenticationServiceTest.php
@codecov
Copy link
Copy Markdown

codecov Bot commented May 6, 2026

Codecov Report

❌ Patch coverage is 81.25000% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.47%. Comparing base (0d4c52f) to head (3c20d63).
⚠️ Report is 18 commits behind head on main.

Files with missing lines Patch % Lines
...ice/Integration/Jira/JiraAuthenticationService.php 76.92% 3 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main     #285      +/-   ##
============================================
- Coverage     81.49%   81.47%   -0.02%     
- Complexity     2579     2584       +5     
============================================
  Files           172      172              
  Lines          7107     7116       +9     
============================================
+ Hits           5792     5798       +6     
- Misses         1315     1318       +3     
Flag Coverage Δ
integration 47.41% <0.00%> (-0.07%) ⬇️
unit 50.59% <81.25%> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

…ne yaml

Two review-feedback items on PR #285:

* JiraAuthenticationServiceTest::throwUnauthorizedRedirectIncludesOriginalException
  previously bare-returned in the catch and silently passed if the method
  stopped throwing. Restores `$this->fail()` after the try/catch with a
  scoped `@phpstan-ignore-next-line deadCode.unreachable` comment so the
  test detects regressions while still satisfying PHPStan's `never`-return
  narrowing.

* phpstan-baseline.neon now uses `ignoreErrors: []` instead of an empty
  scalar key, which NEON parsed as `null` and could surprise downstream
  consumers expecting an array.

Signed-off-by: Sebastian Mendel <github@sebastianmendel.de>
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented May 8, 2026

CybotTM added a commit that referenced this pull request May 8, 2026
… rule

Per gemini-code-assist review on PR #284: rather than adding 121 baseline
entries (242 errors total) for the PHPUnit 13 + phpstan-phpunit stub
mismatch on the legacy `->method('x')->with(...)->willReturn(...)` mock
chain, switch to two targeted message-restricted ignoreErrors rules in
phpstan.neon. Comment in-place documents the deprecation timeline (12.5.11
introduced, 14 removes) and that a dedicated test-modernization PR will
migrate ~143 call sites before the eventual PHPUnit 14 bump.

Net: phpstan-baseline.neon goes from 121 → 40 entries (242 → 80 errors).
The remaining 80 are not new — they're the same set PR #285 is clearing
in source; this PR will rebase against that once it merges and the
baseline should converge to zero plus the two scoped rules above.

Signed-off-by: Sebastian Mendel <github@sebastianmendel.de>
@CybotTM CybotTM merged commit dd64f00 into main May 8, 2026
22 of 24 checks passed
@CybotTM CybotTM deleted the chore/phpstan-baseline-cleanup branch May 8, 2026 09:30
CybotTM added a commit that referenced this pull request May 8, 2026
… rule

Per gemini-code-assist review on PR #284: rather than adding 121 baseline
entries (242 errors total) for the PHPUnit 13 + phpstan-phpunit stub
mismatch on the legacy `->method('x')->with(...)->willReturn(...)` mock
chain, switch to two targeted message-restricted ignoreErrors rules in
phpstan.neon. Comment in-place documents the deprecation timeline (12.5.11
introduced, 14 removes) and that a dedicated test-modernization PR will
migrate ~143 call sites before the eventual PHPUnit 14 bump.

Net: phpstan-baseline.neon goes from 121 → 40 entries (242 → 80 errors).
The remaining 80 are not new — they're the same set PR #285 is clearing
in source; this PR will rebase against that once it merges and the
baseline should converge to zero plus the two scoped rules above.

Signed-off-by: Sebastian Mendel <github@sebastianmendel.de>
CybotTM added a commit that referenced this pull request May 8, 2026
…rvice stdClass narrowing

Resurfaced after rebasing onto main, where #284 added two ignoreErrors
patterns for InvocationStubber::with()/willReturn() that this branch's
mock-to-stub sweep already eliminated, and where #285's stronger Jira
return narrowing made four runtime assertions tautological.

  - JiraHttpClientService::getOAuthConsumerKey: drop ?? '' on
    ticketSystem->getLogin() — the entity setter is now string-typed, so
    the coalesce branch is unreachable.
  - JiraTicketServiceTest: drop assertInstanceOf(stdClass::class, ...)
    in createTicket / searchTickets / getTicket / addComment tests —
    the service signatures already declare : stdClass.
  - phpstan.neon: drop the InvocationStubber::with() and willReturn()
    on mixed ignore patterns — every with()-on-stub call site was
    converted to createStub(...)->method(...)->willReturn(...) in this
    branch's earlier mock-to-stub sweep.

Signed-off-by: Sebastian Mendel <github@sebastianmendel.de>
CybotTM added a commit that referenced this pull request May 8, 2026
…ne cleanup, fully-green PHPUnit (#288)

## Description

Comprehensive PHP modernization pass against the timetracker code base.
Brings the suite to a fully-green PHPUnit 12 state, eliminates 16
entries from the PHPStan baseline by fixing the underlying type issues,
adds `#[\SensitiveParameter]` to every credential-bearing method, and
aligns the Rector and PHP-CS-Fixer configs with the project's actual PHP
8.5 / Symfony 8 / PER-CS targets.

No production behaviour changes — every fix preserves runtime semantics;
only types, mocks and tooling configs move.

**Rebased onto `main` after #284 (PHPUnit 13 upgrade) and #285 (baseline
cleanup) merged.** All 5 commits carry `Signed-off-by:` (DCO).

## Type of Change

- [x] Code refactoring
- [x] Performance improvement (test suite — fewer mock allocations,
earlier PHPUnit 13 readiness)
- [ ] Breaking change

## Changes Made

### Production hardening
- **`#[\SensitiveParameter]` (PHP 8.2+)** — annotated 14 password /
token / OAuth-secret parameters across `LdapClientService::setUserPass`,
`ModernLdapService::{authenticate,validateInput}`,
`JiraOAuthApiService::{getFetchAccessTokenClient,getClient,fetchOAuthAccessToken,storeToken,getOAuthAuthUrl}`,
`TokenEncryptionService::{encryptToken,decryptToken,rotateToken}`, plus
`TicketSystem::setPassword` and
`UserTicketsystem::set{AccessToken,TokenSecret}`. Prevents secrets from
leaking into stack traces / Sentry / Monolog output.
- **Entity setter typing** — `TicketSystem`
`set{Name,Url,Login,Password,TicketUrl,BookTime}`,
`UserTicketsystem::set{AccessToken,TokenSecret}`, plus `User::$id`
(`?int`). Replaces the legacy untyped Doctrine getters/setters that
bypassed strict typing at call sites.
- **Jira service return narrowing** —
`JiraTicketService::{createTicket,searchTickets,getTicket,updateTicket,addComment}`
previously declared `: mixed`/`: object`; now return `: stdClass` (the
truthful runtime type from `json_decode($body, false)`).
- **`JiraWorkLogPayloadDto`** (`final readonly`, namespace
`App\DTO\Jira`) replaces `array $data`/`: mixed` in
`JiraWorkLogService::{prepareWorkLogData,createWorkLog,updateWorkLog}`
with a typed payload + serializer; removes the now-dead `is_object()`
guard in `updateEntryWorkLog`.
- **`OptimizedEntryRepository::findByFilterArrayOptimized`** — promoted
`array $filter` to a precise PHPDoc array-shape (`{user_id?: int,
customer_id?: int, ...}`) so PHPStan can verify each downstream
`ArrayTypeHelper::getInt` call.
- **Two genuine bugs unmasked by tightened types** — removed dead `null
=== $ticketUrl` branch in `Entry::getTicketSystemIssueLink` and dead `??
''` on `JiraHttpClientService::getOAuthConsumerKey` (the entity setter
is now string-typed, so the coalesce branch is unreachable).
- **Deprecation cleanup** —
`ExportService::getEntries{Ticket,Worklog}Url` no longer uses null as an
array-offset (cache key now `getId() ?? ''`);
`TimeCalculationService::formatDuration` swaps an implicit float `%` int
for `(int) floor(fmod(...))`.

### PHPUnit 12/13 cleanup
| Before | After |
|---|---|
| 1 error | **0** |
| 14 warnings | **0** |
| 6 deprecations | **0** |
| 495 notices (1698 occurrences) | **0** |
| 10 risky | **0** |

- **`createMock` → `createStub`** for 1698 sites across 29 test files
where no expectations are set (PHPUnit 12 distinguishes mocks from
stubs). 19 classes received `#[AllowMockObjectsWithoutExpectations]` at
class level instead, where the same fixture is reused with mixed
semantics across many tests.
- **Stripped 67 deprecated `->with(...)`** calls from stub-style chains
(deprecated in 12, hard error in 14). Removes the two
`InvocationStubber::with()` / `willReturn() on mixed` ignore patterns
from `phpstan.neon` introduced by #284 — every `with()`-on-stub call
site is now properly migrated.
- Converted `$this->createStub(...)` → `self::createStub(...)` to
satisfy PHPStan's static-call requirement.
- **Performance test cleanup** — replaced naive `unlink + rmdir` with
`Symfony\Filesystem::remove()`; clears 14 `Is a directory` / `Directory
not empty` warnings.
- **Exception-handler restoration** — added `tearDown {
restore_exception_handler(); parent::tearDown(); }` to
`EntryRepositoryExportTest`, balancing Symfony's `bootKernel()`
registration; clears 4 risky markers.
- **Real assertion** —
`EntryRepositoryFullIntegrationTest::testFindOverlappingEntriesExcludesSpecifiedId`
now uses a single `assertNotContains` instead of an
empty-result-tolerant foreach.
- **JiraOAuthApiService early-return tests** — dropped the
`expectNotToPerformAssertions()` calls; the existing `mock->method(...)`
calls are themselves the verifications PHPUnit was counting.
- Deleted redundant
`EntryTest::testGetTicketSystemIssueLinkReturnsTicketWhenNullTicketUrl`
(sibling empty-string test already covers the same intent under the
now-non-nullable `getTicketUrl(): string` contract).
- Removed 4 tautological `assertInstanceOf(stdClass::class, $result)`
calls in `JiraTicketServiceTest` since the service now declares `:
stdClass`.

### Tooling alignment
- **Rector** — bumped `LevelSetList::UP_TO_PHP_84` → `UP_TO_PHP_85`,
dropped `SymfonySetList::SYMFONY_73` (per-version sets are `@deprecated`
upstream) in favour of `->withComposerBased(symfony: true)` which
auto-detects the installed Symfony 8 line, and added
`->withAttributesSets(symfony: true)` so future annotation→attribute
migrations run automatically. Applied the one finding the bump surfaced
(`array_values($x)[0]` → `array_first($x)` in `SaveContractAction.php`).
- **PHP-CS-Fixer** — bumped `@PHP84Migration` → `@PHP85Migration`.
Codebase was already 8.5-clean: zero diff from the bump itself.
- **Stale config cleanup** — deleted 4 unreferenced files (root
`rector.php` placeholder,
`config/quality/{phpstan.dist.neon,phpstan-baseline.neon,phpstan-phpat.neon}`)
plus 90 orphaned PHPStan cache files under `config/quality/var/`
(already in `.gitignore` line 62, so they shouldn't have been tracked).
Verified no hits in CI/Makefile/composer for the deleted files.

## Testing

- [x] **`make test`** (Docker) — `Tests: 1922, Assertions: 5501,
Skipped: 1`, 0 errors / 0 failures / 0 warnings / 0 deprecations / 0
notices / 0 risky (the 1 skip is intentional and pre-existing).
- [x] **`composer check:all`** — PHPStan level 10 + bleedingEdge `[OK]
No errors` (311 files); PHPat `[OK] No errors`; PHP-CS-Fixer clean; Twig
lint clean.
- [x] **`bin/rector … --dry-run`** — clean.
- [x] **`composer security-check`** — no advisories.
- [x] **`npm run build`** — webpack compiled successfully, no warnings,
2106 assets emitted.
- [x] **`make e2e`** (Playwright on ephemeral compose stack) — **118
passed, 1 failed, 2 skipped**. The single failing test
(`settings.spec.ts:142 › should save show_empty_line setting`) is a
parallel-state flake: it passes 3/3 in isolation; the failure is a race
when the full suite runs concurrently. Not a regression caused by this
branch.
- [x] Manual testing completed.

## Code Quality

- [x] Code follows project coding standards (`@PER-CS:risky` +
`@PHP85Migration`)
- [x] Self-review completed
- [ ] Documentation updated — none needed; all changes are internal
types and tooling
- [x] No breaking changes — public API is unchanged; the `: stdClass`
narrowing on `JiraTicketService` is a covariant tightening (callers
already accessed `$result->key`/`$result->total` style on `stdClass`)

## Migration Notes

- **`composer rector` cannot be safely tested via `composer rector --
--dry-run`** — composer doesn't forward flags through that script, so
the dry-run flag is silently dropped and rector runs in apply-mode. Use
`bin/rector process src --config=config/quality/rector.php --dry-run`
directly when previewing.
- **Tests must run inside the Docker `app-dev` container** (per the
existing `make test` target). The host typically lacks `pdo_mysql` and
the `db_unittest` hostname only resolves on the compose network.
- **All 5 commits carry `Signed-off-by:` (DCO)**. The branch was rebased
onto `main` after #284 and #285 merged.

## Checklist

- [x] I have read the
[CONTRIBUTING](https://github.com/netresearch/timetracker/blob/main/CONTRIBUTING.md)
guidelines
- [x] I agree to follow the [Code of
Conduct](https://github.com/netresearch/timetracker/blob/main/CODE_OF_CONDUCT.md)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants