MMOframework
Pourquoi l'IA Playground Bibliothèques Documentation Rechercher Agence
FR EN
Index

BoardSync

MMO\git\helpers · 1 symbole · générée le 27 Oct 2026

class

BoardSync

BoardSync.php:47

Merge two repositories of mmo.client's kanban entities (KanbanBoard, KanbanTicket, KanbanComment) into each other, when asked: a local one -- a JsonRepository, mmo.mongodb's MongoRepository -- where the tickets are worked on, and a remote one, usually a GitHubRepository (a GitHub project, or a repository's issues). Each side keeps its own ids; a sync writes on each side what the other changed since the last one.

class MMO\git\helpers\BoardSync
Espace de noms
MMO\git\helpers
Voir
$match (the same title, by default) before any is made. - A ticket removed on one side is removed on the other, unless the other changed it since (a conflict), or, $deletions is false. A remote ticket missing from what GitHub lists is not taken for removed on that alone: GitHub lists a draft just made minutes later. It is removed once GitHub confirms it is gone, asked for it by what finds it without the listing (a project item's node id, kept in the state while it was listed: GitHubRepository::isGone), and never while it is younger than $grace when a sync made it; until then the pair is kept and the report says so (unconfirmed), nothing is written on either side. - The statuses are paired by $statuses, else by their label, else by their id, else by their type; the categories and the labels by their label. A parent, or a ticket a constraint names, is the paired ticket. - The comments are merged the same way, ticket by ticket, when the remote ticket has a discussion. A project's draft has none: one whose local ticket has comments becomes an issue first ($issues), else its comments stay local. A comment made on GitHub says who wrote it locally when it is not the token's owner; only the token's owner's comments are changed or removed there. - What one side cannot keep (a draft's labels) is not a change: the remote side is read again after it is written, and what it kept is what the next sync compares with -- a draft GitHub does not list yet is what it holds of what was written (ProjectBoard::save: no labels), not what was written. The local ticket keeps its labels.
### How it merges It is a three-way merge: [`$state`](https://mmoframework.fr/fr/documentation/mmo.git/php/helpers/BoardSync.md#boardsync-state) keeps, for each pair of tickets (a local id, a remote id), what both sides held at the end of the last sync, field by field. A field changed on one side only is written on the other; changed on both to different values, it is a conflict, settled by [`$conflicts`](https://mmoframework.fr/fr/documentation/mmo.git/php/helpers/BoardSync.md#boardsync-conflicts) (left as it is on both sides by default, and reported until one side is changed to match the other). - A ticket on one side only is made on the other. The first time, the tickets of both sides are paired by Nothing is written on a dry run: the report says what would be.
$local = new JsonRepository(['boards' => 'data/boards.json', 'tickets' => 'data/tickets.json', 'comments' => 'data/comments.json'], COLLECTIONS); $github = new GitHubRepository(COLLECTIONS, project: 'https://github.com/orgs/oviepro/projects/28'); $report = (new BoardSync($local, $github, 'data/main.sync.json'))->run(dryRun: true); // ['local' => ['created' => [...], 'updated' => [...], 'removed' => [...]], 'remote' => [...], 'conflicts' => [...], ...]

Index

DEFAULT_STATUSESa board without statuses has <mmo-kanban>'s
FIELDSa ticket's fields that are merged: the kanban's names
LOCALthe side a conflict is settled for, or neither
REFERENCESthe fields naming other tickets: written once the tickets they name are on both sides
REMOTE
SKIP
conflicts
deletions
grace
issues
local
localBoard
maps
match
matchId
onStep
remote
remoteBoard
report
state
stateFile
statuses
attribute($comment, $viewer)
board($side)
boards($dryRun)a side without the board gets it, with the other's settings
buildMaps()
collection($side, $class)
comments($side, $ticket)
data($ticket)
discusses($remoteTicket)
discussions()merge the comments of each pair of tickets whose remote one has a discussion
explicitRemoteIds($remoteStatuses)
find($id)Find a ticket by its id on either side, from the state of the last sync (nothing is read from GitHub).
FromProfile($profile, $options) staticA sync as the configuration's kanbanSync describes it: each profile names the repositories of the configuration (local, remote), the board, the remoteBoard, the state file, and `conflic…
issues($dryRun)Make an issue of each remote draft whose local ticket has comments: a draft has no discussion.
Lines($report) static
lock()
makesIssues()
merge($created, $dryRun)Merge the fields of each pair: what changed on one side since the last sync is written on the other.
mergeComments($l, $r, $local, $remote, $viewer)
merged($dryRun)
name($side)
other($side)
pairAndCreate($dryRun)Pair the tickets, make on each side those it lacks, and remove those removed on the other.
pairList($from, $to, $explicit, $byType)
ProfileOf($board) static
Profiles() static
readState()
refs()Write on each local ticket what its remote one is called and where it is (KanbanTicket's ref and url, which are not merged): gh#255, for a person holding GitHub's number to find the local tic…
reload()read the remote side again: what it kept of what was written, and the ids it gave
remoteRef($remoteTicket, $url)
removeTicket($side, $ticket)remove a ticket and the comments its side holds already
run($dryRun)Merge the two boards into each other.
sideReport()
step($step, $done, $total)Tell $onStep where the run is.
tickets($side)
toIssue($remoteTicket)
translate($data, $from, $only)
translateCondition($node, $pairs)
translateLabels($labels, $from)
unconfirmed($l, $r)
unpair($local)
writeRefs()Write the refs of the last sync's pairs on the local tickets (refs), without syncing: GitHub is read, not written.
writeState()write through a temporary file: a crash never leaves half a state
__construct($local, $remote, $stateFile, $localBoard, $remoteBoard) final

Constantes6

DEFAULT_STATUSES

a board without statuses has <mmo-kanban>'s

const DEFAULT_STATUSES = […5 items]

FIELDS

a ticket's fields that are merged: the kanban's names

const FIELDS = […10 items]

LOCAL

the side a conflict is settled for, or neither

const LOCAL = 'local'

REFERENCES

the fields naming other tickets: written once the tickets they name are on both sides

const REFERENCES = ['parent', 'constraint']

REMOTE

const REMOTE = 'remote'

SKIP

const SKIP = 'skip'

Propriétés16

conflicts

string $conflicts = 'skip'
Type
string

deletions

bool $deletions = true
Type
bool

grace

int $grace = 3600
Type
int

issues

bool $issues = true
Type
bool

local

protected MMO\core\classes\aRepository $local
Type
aRepository

localBoard

protected string $localBoard
Type
string

maps

protected array $maps = []
Type
array

remote

protected MMO\core\classes\aRepository $remote
Type
aRepository

remoteBoard

protected string $remoteBoard
Type
string

report

protected array $report = []
Type
array

state

protected array $state = […5 items]
Type
array
Voir
$stateFile; made: when a sync made the remote ticket of a pair, by local id (for $grace); locators: what finds a pair's remote ticket on GitHub without listing the board (GitHubRepository::locator), by local id

stateFile

protected string $stateFile
Type
string

statuses

array $statuses = []
Type
array

Méthodes43

attribute($comment, $viewer)

BoardSync.php:875
protected function attribute(MMO\client\entities\KanbanComment $comment, ?string $viewer): string
$commentKanbanComment
$viewernull | string
retournestringa local comment's body as it is written on the remote side: saying who wrote it, when it is not who writes there

boards($dryRun)

BoardSync.php:278

a side without the board gets it, with the other's settings

protected function boards(bool $dryRun): void
$dryRunbool
retournevoid

buildMaps()

BoardSync.php:299
protected function buildMaps(): array
retournearrayarray<string, ?string>> the statuses and categories of each side, by the other's ids

collection($side, $class)

BoardSync.php:906
protected function collection(string $side, string $class): MMO\core\interfaces\iCollection
$sidestring
$classstring
retourneiCollectionthe side's collection of $class
lèvewhen it has none

comments($side, $ticket)

BoardSync.php:890
protected function comments(string $side, string $ticket): array
$sidestring
$ticketstring
retournearrayKanbanComment> a ticket's comments on a side, by id, oldest first

data($ticket)

BoardSync.php:603
protected function data(MMO\client\entities\KanbanTicket $ticket): array
$ticketKanbanTicket
retournearraythe merged fields of a ticket, as the kanban names them, the labels in order

discusses($remoteTicket)

BoardSync.php:884
protected function discusses(string $remoteTicket): bool
$remoteTicketstring
retourneboolwhether the remote ticket has a discussion: a GitHub project's draft has none

discussions()

BoardSync.php:768

merge the comments of each pair of tickets whose remote one has a discussion

protected function discussions(): void
retournevoid

explicitRemoteIds($remoteStatuses)

BoardSync.php:317
protected function explicitRemoteIds(array $remoteStatuses): array
$remoteStatusesarray
retournearraystring> $statuses, its remote statuses given by their id

find($id)

BoardSync.php:731

Find a ticket by its id on either side, from the state of the last sync (nothing is read from GitHub).

function find(string $id): array
$idstringa local id or a remote one (255, #255, d277275317: either side), a ref (gh#255: the remote side), or a GitHub issue's page
retournearraystring, remote: string, ref: ?string, title: string, status: ?string, side: string}> the pairs it names: side says which side's id it is (both sides may have a ticket of that number)
BoardSync::FromProfile('my.oviepro-deploy')->find('gh#255'); // [['local' => '260', 'remote' => '255', ...]]

FromProfile($profile, $options)

staticBoardSync.php:145

A sync as the configuration's kanbanSync describes it: each profile names the repositories of the configuration (local, remote), the board, the remoteBoard, the state file, and conflicts, deletions, issues, statuses, matchId, grace (the properties of the same names).

static function FromProfile(?string $profile, array $options = []): static
$profilenull | stringthe profile's key, null for none (every option given or by default)
$options = []array
retournestatic
lèvefor an unknown profile, a repository missing, an unknown conflicts
BoardSync::FromProfile('my.oviepro-deploy')->run(dryRun: true);BoardSync::FromProfile(null, ['local' => 'tickets', 'remote' => 'github', 'conflicts' => 'local']);

issues($dryRun)

BoardSync.php:636

Make an issue of each remote draft whose local ticket has comments: a draft has no discussion. The pair names the issue then; what the draft could not keep (its labels) is written from the local ticket, and so are the remote tickets naming the draft (a parent, a constraint).

protected function issues(bool $dryRun): void
$dryRunbool
retournevoid

Lines($report)

staticBoardSync.php:220
static function Lines(array $report): array
$reportarraywhat run returned
retournearraythe report, for a person: one line per ticket written, conflict and note
BoardSync::Lines($sync->run(true)); // ['remote: would be created 12 Fix the login', ...]

makesIssues()

BoardSync.php:672
protected function makesIssues(): bool
retourneboolwhether the remote side's drafts can become issues

merge($created, $dryRun)

BoardSync.php:475

Merge the fields of each pair: what changed on one side since the last sync is written on the other.

protected function merge(array $created, bool $dryRun): void
$createdarray
$dryRunbool
retournevoid

mergeComments($l, $r, $local, $remote, $viewer)

BoardSync.php:792
protected function mergeComments(string $l, string $r, array $local, array $remote, ?string $viewer): void
$lstring
$rstring
$localarray
$remotearray
$viewernull | stringwho writes on the remote side: the only one whose comments can be changed there
retournevoid

pairAndCreate($dryRun)

BoardSync.php:360

Pair the tickets, make on each side those it lacks, and remove those removed on the other.

protected function pairAndCreate(bool $dryRun): array
$dryRunbool
retournearraystring> the pairs made this run, by local id => the side the ticket came from

pairList($from, $to, $explicit, $byType)

BoardSync.php:334
protected function pairList(array $from, array $to, array $explicit = [], bool $byType = true): array
$fromarraya list of statuses or categories
$toarray
$explicit = []array
$byType = trueboolwhether an item without a match takes the first of $to of its type
retournearray?string> each id of $from => the id of $to it is, null for none

ProfileOf($board)

staticBoardSync.php:189
static function ProfileOf(string $board): ?string
$boardstringa local board's name
retournenull | stringthe key of the profile syncing it (its board, else its key), null when none does

Profiles()

staticBoardSync.php:179
static function Profiles(): array
retournearrayarray<string, mixed>> the configuration's kanbanSync profiles, by key

readState()

BoardSync.php:930
protected function readState(): array
retournearrayarray<string, string>, base: array<string, array>, comments: array<string, array>, made: array<string, string>, locators: array<string, string>}

refs()

BoardSync.php:691

Write on each local ticket what its remote one is called and where it is (KanbanTicket's ref and url, which are not merged): gh#255, for a person holding GitHub's number to find the local ticket on the board (its filter) or by find. A local ticket paired no more loses them. Only a remote side naming its tickets (remoteRef) does it.

protected function refs(): void
retournevoid

reload()

BoardSync.php:920

read the remote side again: what it kept of what was written, and the ids it gave

protected function reload(): void
retournevoid

remoteRef($remoteTicket, $url)

BoardSync.php:757
protected function remoteRef(string $remoteTicket, ?string $url = null): ?array
$remoteTicketstringa remote ticket's id
$url = nullnull | stringits page, when the board read gives one
retournenull | arraystring, url: ?string}|null what it is called and where it is (GitHubRepository::ref); null: the remote side does not name its tickets

removeTicket($side, $ticket)

BoardSync.php:623

remove a ticket and the comments its side holds already

protected function removeTicket(string $side, MMO\client\entities\KanbanTicket $ticket): void
$sidestring
$ticketKanbanTicket
retournevoid

run($dryRun)

BoardSync.php:205

Merge the two boards into each other.

function run(bool $dryRun = false): array
$dryRun = falseboolonly say what would be done
retournearray{dryRun, local, remote, conflicts, unconfirmed, notes}: what was done on each side (created, updated, removed tickets, comments, and the drafts made issues), the conflicts left, the pairs whose remote ticket GitHub's listing left out without GitHub confirming it is gone (kept, not removed: {ticket, remote, title, message}), and what could not be synced
lèvewhen a side refuses a change: what was done before is kept in the state

sideReport()

BoardSync.php:925
protected function sideReport(): array
retournearraylist<array>, updated: list<array>, removed: list<array>, comments: array{created: int, updated: int, removed: int}, issues: list<array>}

tickets($side)

BoardSync.php:614
protected function tickets(string $side): array
$sidestring
retournearrayKanbanTicket> the side's tickets of its board, by id

toIssue($remoteTicket)

BoardSync.php:680
protected function toIssue(string $remoteTicket): string
$remoteTicketstringa draft
retournestringits id now it is an issue

translate($data, $from, $only)

BoardSync.php:563
protected function translate(array $data, string $from, bool $only = true): array
$dataarrayfields of a ticket of the $from side
$fromstring
$only = trueboolwhether to translate the fields given only (a field missing stays missing)
retournearraythem as the other side names them: its statuses, categories, labels and tickets

translateCondition($node, $pairs)

BoardSync.php:593
protected function translateCondition(mixed $node, array $pairs): mixed
$nodemixed
$pairsarray
retournemixedthe condition, the tickets it names the other side's (one never synced is left as it is)

translateLabels($labels, $from)

BoardSync.php:580
protected function translateLabels(array $labels, string $from): array
$labelsarray
$fromstring
retournearraythe labels as the other side names them: by their label, else as they are called

unconfirmed($l, $r)

BoardSync.php:453
protected function unconfirmed(string $l, string $r): ?string
$lstringa pair's local ticket
$rstringits remote ticket, which the remote side's listing leaves out
retournenull | stringwhy it may be there still, and its local ticket is not to be removed (nor it made again); null: it is gone indeed -- a remote side other than GitHub lists what it holds

writeRefs()

BoardSync.php:711

Write the refs of the last sync's pairs on the local tickets (refs), without syncing: GitHub is read, not written. What a sync does at its end; for a board synced before the refs were written.

function writeRefs(): void
retournevoid

writeState()

BoardSync.php:962

write through a temporary file: a crash never leaves half a state

protected function writeState(): void
retournevoid

__construct($local, $remote, $stateFile, $localBoard, $remoteBoard)

finalBoardSync.php:130
final function __construct( MMO\core\classes\aRepository $local, MMO\core\classes\aRepository $remote, string $stateFile, string $localBoard = 'main', ?string $remoteBoard = null )
$localaRepositorythe repository the tickets are worked on in
$remoteaRepositorythe one it is synced with: a GitHubRepository, or any repository of kanban entities
$stateFilestringa JSON file keeping what both sides held at the end of the last sync ({root} and the like are expanded): the pairs of tickets and comments, and their fields. Lost, the next sync pairs the tickets by $match again, and settles every difference as a conflict.
$localBoard = 'main'stringthe name of the local board
$remoteBoard = nullnull | stringthe name of the remote one; null: a GitHubRepository's board, else $localBoard