This file tells you why the JSON shape is the way it is. It also gives details for each command that script authors will want to know.
The README shows how to use --json,
shows an example, and documents the exit codes.
The JSON Schema at schema/json-output/v1.json
is the authoritative contract for each field. The markfluence schema command
also prints it.
schema_version is 1. It increases only for a change that can break a
consumer.
These changes are compatible, and schema_version stays the same:
-
a new key on the envelope, on a result, on a summary, or on the error object;
-
a constraint that is made looser;
-
a new command, which adds a value to the
commandenum. Only the new command's own output carries that value, so no consumer of an existing command sees it; -
a new value of
fieldin adiffresult. It is a string rather than an enum for this reason: a frontmatter field that markfluence learns later is reported there too.
These changes break compatibility, and schema_version increases:
- a key that is removed or renamed;
- a key whose type or meaning changes;
- a key that can be
nulland could not before; - a new value in any other enum, such as
codeor a result'sstatus. A consumer can reasonably handle each value of those, and a new one would reach a consumer of an existing command.
Thus a consumer must ignore a key that it does not know. The published schema
is open for this reason: an object can have keys that the schema does not list.
If you validate the output, validate against the schema that
markfluence schema prints, which is the schema of the binary that you run.
A copy from an older release does not list the newer keys, but it still
accepts them.
markfluence's own tests validate against a closed copy of the schema, which refuses any key that the schema does not list. Thus a key cannot get into the output before it gets into the schema.
-
Stable for each command. Each command always writes the same keys in the same shapes. An empty value is
nullor[]. The set of keys is different for each command. Any change that breaks compatibility increasesschema_version. See Compatibility for which changes those are. -
rootslists each different documentation root that the command resolved, in sorted order. It is[]for a command that has no per-file root, such asfindorsearch. It is also[]for a preflight failure that stopped before root resolution.schemawrites no envelope at all, so it has norootskey. -
warningsholds warnings about the invocation, and not about a page or a file. Now, both such warnings come from reading credentials: the permission warning for a file that holds your API token, and the warning about a cloud ID that markfluence ignored (see credentials.md). The warnings of a result are on that result. This field is for a warning that belongs to no result. It is[]when there is nothing to report. It is also on the error object on stderr. A fatal failure writes no envelope, and a credential failure is exactly the run where a warning about that file is important. -
Status verbs are different for each command:
update:published,skippedcreate:created,not_createdcheck:clean,warnings,brokenattachment-upload:created,updated,skippedattachment-download:downloaded,skippedexport:wrote,skipped, or""for a page that failed. Each attachment in itsattachmentsarray hasdownloaded,skipped,skipped_unreferenced, orfailed.
Every command in the list, except
export, also hasfailed.The results of the other commands hold data only, and they have no status verb.
childrenhas astatusfield, but that is the content status of Confluence (current,archived), and not a status verb. -
There is one result for each target, and the target is different for each command:
page-info,read: the page. There is always one.export: the page. There is one result for each page that it exported, so--depthand--spacegive more than one.update,create,check: the file.- the three
attachment-*commands: the attachment. Thus.results[] | .filenameworks, andsummary.totalis the count of attachments. If markfluence cannot find the page, the result is a single failure that has apage_idand nofilename.
exportputs the files that it wrote in anattachmentsarray on its page result, asupdateandcreatedo. -
metadata_sourceonupdateandcreatetells you which location supplied the page metadata of that file. The values are"frontmatter","manifest"(apages:entry inmarkfluence.yaml), ornullwhen nothing claimed the file. When both locations supply metadata, it reports"frontmatter", because frontmatter wins every field that it can win.This field exists because otherwise, "why did this publish to that page?" has only one answer: do the resolution again by hand. That is difficult from a CI log. On
update, anullsource comes withstatus: skipped. Nothing claims the file, and that is not a failure, because a repository can correctly hold Markdown that nobody publishes.diffalso reports it.diffalso gives a per-fieldsourceon each frontmatter difference. That field does show the case where both locations supply the value ("frontmatter and markfluence.yaml"). To correct a field that two locations supply, you must edit two files. If markfluence told you only one of them, the value would come back on the next run. -
baseandbody_changedonupdatereport the data that the moved-page check and the unchanged-body check had (#149).baseis the merge base that an earliercreate,update, orexportrecorded locally for that file. It isnullwhen markfluence could not use a base: there was no log, no line for the file, or a line for a different page. Thatnullis the signal that a consumer needs, and a person does not. It means that the checks could not run, so markfluence published the file with no check.body_changed: falsemeans that the page already held what the file renders to. Thus markfluence skipped the bodyPUT, andversion.previousis equal toversion.new. The attachment, width, and label passes still ran. Thus the result can bepublishedwith no new version.body_changedisnullwhen the check did not run: there was no base, the base had no sha, or you gave--force.--forcealways publishes and uses neither check. -
movedonupdateis the parent of the page before and after a move to theparentthat the file declares (#10). It isnullwhen the page did not move.fromortoisnullfor the top of the space. A move alone makes the resultpublishedwith no new version, because a move does not change the page version. On a failed result,movedis set if the move happened before the failure, since the page is then in its new place. -
code: "CONFLICT"is the refusal to overwrite a page that has a newer version than your copy. It is notVALIDATION, because nothing in the file is wrong. To correct it, export the page again or give--force. The run exits with a code that is not zero, and the other files in the batch are not affected. -
The
brokenstatus ofcheckisok: falsewith noerrorand nocode. For every other failure, those fields hold an operational error. Forbroken, thebrokenandwarningsarrays already give all the information, so there is no operational error to attach. Only thefailedstatus ofchecksetserrorandcode, as every other command does. Afailedfile never got to the converter at all.check --show-htmladds adebug: { html, attachments } | nullfield. It is filled in only for a file that got to the converter.htmlis exactly what the converter made, with no indents, because it must agree with whatupdateandcreatewould publish. -
diffgives its answer in two parts, and under--jsonboth parts are in the payload.diffis the unified diff of the body as one string. It never has color, and it is""when the bodies agree.frontmatteris an array of the fields that are different. It is[], nevernull.differsanswers the question that the exit code answers.body_differsis a narrower question: "is there a patch?" For a file where only the title changed,body_differsisfalseanddiffersistrue.A row with
comparable: falsehas anullconfluencevalue, because the read of the page failed. markfluence reports that row, but the row does not setdiffers. Nobody asked the page, so nothing can say that the page disagrees. markfluence compares only the fields that the file declares. -
A compound value is an object, and never a display string. Examples are
version,page_width, and thecreatedandupdatedauthor stamps onpage-info. -
Two fields use the word "status", and they are different things.
content_statusonpage-infois the content status of Confluence:current,archived, ortrashed.page_statusis the colored lozenge next to the page title. Its shape depends on what the command can say about it:updateandcreatereport{name, action}.actionissetorunchanged.unchangedis useful because a status write gives the page a new version. Thusunchangedis the evidence that a run that changed nothing added no version.page-infoandreadreport only the name.
On
updateandcreate,page_statusisnullwhen the file declares no status, or when markfluence could not assert it. The second case is a warning to act on. Onpage-infoandread, it isnullwhen the page has no status, or when the read failed.page-infoalso haspage_status_available. This is the list of statuses that the account that ran the command can give to that page. It is[]when the page can be given no status, andnullwhen that read failed. It is not a property of the space. Confluence makes the list for each page and each account. Thus another page in the same space can have more statuses or fewer. Confluence refuses a write of a status that is not in the list. The list also never includes a custom status of your own, because nobody else can use it. -
The preflight abort of
createoccurs when any file fails, and then markfluence creates nothing. The result lists every input file. The failed files have anerror, and the other files arenot_created. The summary setssummary.aborted: true. -
Warnings and notices about broken images and links are data in the
warningsandbrokenarrays on each result. They are not log lines on stderr. -
space-infocan fail in 3 independent places, and each failure is visible.accessisnullwhen markfluence could not read the permissions of the space. That means "not known", and never "can do nothing". Its fieldcreate_pagesis not calledwrite, on purpose, because permission to edit a page that exists is not a space grant at all.pagesandrecentare bothnullwhen the page walk failed. They are never partial, because a wrong count is worse than no count.page_statusesis always an object with asourcefield."space"is the list that the space has configured, which only space admins can read."page"is the list that the running account can set onprobe_page_id, and it is not the list of the space.nullmeans that markfluence could read neither list.
The counts in
recentare counts of pages, not edits.pages_createdandpages_touchedoverlap.pages_created_and_touchedreports the intersection, so that nobody adds the two together. -
user-inforeports which question you asked.selfistruefor the form with no argument, which shows the account that owns the credentials. It isfalsewhen you gave an account id. Thus a consumer never has to guess.personal_spaceisnullwhen the account has no personal space. That is a real answer, because a service account has none. You cannot make itskeyfromaccount_id, because personal spaces with an email key and with an account id key are both in use.spacesis present only for the form with no argument. Otherwise it isnull, and it is alsonullwhen the survey failed. It describes the authenticated credentials, and the API route takes no account id. If markfluence reported it next to a different account, it would give that account the access of the caller. Itswritefield lists the spaces where those credentials can create pages. That is a space grant, and not permission to edit a page that exists. -
The discovery commands list what they found.
resultshas one object for each match (find,search,user-find) or for each node (children).summary.totalis that count.The summary of
searchhas two more fields.truncatedmeans that the search got to--limitand more matches were available.skippedcounts index rows that had no page id to report. You can get those rows only with--cqlor--type all. Neither field is a count of matches that you could get if you asked for more.The summary of
user-findhastruncatedfor the same reason, and for a stronger one. ThetotalSizeof the user route gives the count of rows on the page that it just fetched. It does not give the size of the result set. Thus markfluence cannot count the other matches, even in principle. -
user-findgives the Markdown for a mention as a field. A consumer does not have to build it fromaccount_id. It is the same string that the converter writes when it renders a mention out of storage format. Thus if you paste.results[0].mentioninto a body and publish it, it round-trips exactly.To build it by hand, you must know two things. The profile host is Atlassian Home, and not your site. The leading
@is what makes the link a mention, and not a link to the profile of a person. There is notypefield, although the API gives one. Every account givesknown, also automation accounts and page-template accounts, so the field shows no difference.
Errors and exit codes:
-
An operational failure for one file is in
resultsas{ "ok": false, "error": "…", "code": "…" }. The command exits with1if any file failed. -
find,search,user-find,space-info,user-info, andchildren --spacehave no failed result. They name no page, so there is no id to attach a failure to. For an operational failure, they print the same typed error object to stderr and exit with1. There is no envelope on stdout. An emptyresultsarray would be worse than no output, because "no matches" is a real answer that a caller acts on. -
diffuses the exit codes ofdiff(1)instead, and it is the only command that does.0means the same,1means different, and2means any trouble.2includes the operational failures that every other command reports as1. A shell script has only the exit code (if markfluence diff FILE >/dev/null 2>&1; then …). Thus1gives the answer, and not a failure. Trouble that names the page is still aresults[0]failure on stdout. Trouble before that point is still an error object on stderr. Only the code is different. -
A fatal or preflight failure prints a typed error object to stderr and exits with
2. Examples are a bad flag, or a credential that does not resolve. For a bad flag, markfluence has not parsed the command yet, socommandcan be"":{ "schema_version": 1, "command": "update", "error": "…", "code": "CONFIG", "warnings": [] } -
These are the error
codevalues:CONFIG,AUTH,NOT_FOUND,VALIDATION,CONVERT,IO,NETWORK,API,CONFLICT.