Maintaining a FHIR IG publication has two main phases:
- Authoring the IG
- Publishing the IG on the web
The first process is documented elsewhere - see pages in Designers. Once you've finished this process, you have an IG that is building, the community of interest has signed off that it's:
- ready for implementation (sign off according to the agreed process by the publishing authority)
- QA is checked and approved
Typically, the rolling current version is published using the auto-build process on build.fhir.org, but it might be published elsewhere (e.g. github pages, Simplifier, whatever). When you build it locally with the FHIR IG publisher, the last line of output says:
Done. This IG has been built using the 'normal' process for local use. If building to host on an an external website, use the process documented here: https://confluence.hl7.org/display/FHIR/Maintaining+a+FHIR+IG+Publication
This page documents that second process. Note that you don't need to follow this process to publish the content of an IG - The IG consists of a set of files which result from the first process and can be hosted anywhere not follow this process. However, there are additional features that are added at publication to ease the maintenance and adoption, so it's strongly recommended to follow this process.
This second process prepares the content of a web server for hosting the Implementation Guide.
In a typical setup, a web server hosts several IGs, and every time one of those IGs has a new release, the website gets updated:
- the IG release is built and added to the web server in the version folder - for example http://hl7.org/fhir/uv/sdc/STU3/
- if the release is a milestone, the content of the new release is placed in the IG's canonical url folder - for example http://hl7.org/fhir/uv/sdc/
- the IG's history page (that lists the current and past versions of the IG) is updated - for example http://hl7.org/fhir/uv/sdc/history.html
- redirects are added to allow tooling to fetch the content
- the publish box on each IG page is updated
Canonical URLs
Important
Correct canonical URLs are critical for this publication approach. This approach to maintaining FHIR publications assumes that the IG is published at its canonical URL, and maintained there following the rules laid out in this page. The package ID is also fundamental and is cross-checked against the canonical URL. Some additional rules:
- all the IGs maintained on a single server should use either http:// or https:// in their canonical URLs consistently. Mixing http:// and https:// is a recipe for disaster
- changing an IGs canonical URL or package id (even just changing the case) is painful and needs manual editing.
- case matters - package ids should be lowercase, and canonical URLs should be too, but the infrastructure will work if they aren't (just don't change them)
FHIR Publication Directory
The FHIR specifications are published as they are built by the IG publisher - a set of static HTML pages, with support resource instances, and package(s). Some additional supporting material is published to the web:
- navigation files that help humans and software navigate the publication structure
- redirection files that allow the specification to function as an instance of a FHIR Server
- interlinked history web that links across the published versions
The FHIR IG publisher has a special mode that performs almost all of this work and prepares the content for being served on a website - the structure of the website can be either
- (web-root)/[org]/[code] - for example https://profiles.ihe.net/ITI/MHD
- (web-root)/[code] - for example https://fhir.openmrs.org/core
- (web-root)/[org]/[code] (for HL7 IGs) for example http://hl7.org/fhir/uv/sdc/
Directory Structure
The canonical structure is
- working folder: Root, just the local directory that contains the folders of interest
- web-root: the root folder on the web server. The entire contents of this server are synchronised between the local maintenance computer and the webserver that hosts the IG(s) on the web. This folder is usually published at http(s)://{server} but may be published at http(s)://{server}/{path} (e.g. http://hl7.org/fhir or http://fhir.org/guides). The actual name of this folder is irrelevant - user discretion applies
- (org): the organizational section of the IG. This layer is optional
- (ig): the folder that is the root of an IG - where the IG is published. This should be the canonical URL of the IG e.g. the canonical URL for FHIR US Core is http://hl7.org/fhir/us/core, where web-root matches
hl7.org/fhir
, and the (org) isus
, and the (ig) iscore
- (version): the version of the IG - this might be the semver version e.g. 1.1.0, or it might be some code that matches the logical publication name e.g. STU1 or 2022-10
The values of [org] and [ig] must match those obtained from the package id. See publication patterns below.
The publication arrangement is that each version of the IG that has ever been published is found at (canonical)/(version)
, where version is some string that logically identifies the publication e.g. DSTU1
for the first milestone publication.
In addition, there will be the folder (canonical)/{semver}
which has a redirect to (canonical)/(version)
unless version
is the same as server
(can be, but doesn't have to be. HL7 will use semver
in the future)
Then there's the concept of current - which of the publications is the current official copy that implementers should adopt generally. It's not always that last publication, since most organisations publish drafts of updated versions for review (e.g. ballot cycles). So a typical process is that there's a sequence of drafts of various status leading to the publication of a 'milestone' the updates the current version. This current version is published twice: in (canonical)/(version)
, and also in (canonical)
. This means that what is at (canonical)
may either be missing, or it will be the last milestone, and it will change when new milestones are released (where as the content at (canonical)/(version)
will never change meaningfully. it does change a little when new milestones are released - see below)
web-root
This folder contains a few files of interest:
- publish-setup.json - contains information that is relevant for the IG publisher, so it knows how that publication is laid out and what web server is in use. This file is documented below.
- package-feed.xml - an RSS XML document that has an entry for each publication that has been made, so that robots/ fhir tools can easily find the publications. Note that this is an entry for each publication, not each IG. e.g. IGs are published repeatedly, and each time a new version is published, a new version specific reference to the IG is entered into this RSS feed. This RSS feed should be registered in https://github.com/FHIR/ig-registry/blob/master/package-feeds.json - make a PR registering it when the first IG is published
- publication-feed.xml - an equivalent to the package-feed.xml file, but intended for human consumption. e.g this would be registered in an RSS feed reader. The difference between this file and package-feed.xml is that this file contains references to index.html and package-feed.xml contains references to package.tgz.
In addition, the web-root directory should contain an index.html file that is the landing page for the server. Typically, this is authored as part of the overall process of maintaining a wider organizational web site, and the IG publication infrastructure ignores any other content in the web-root folder not documented on this page
(org)
The (org) folder - if it exists - corresponds to an organizational unit of publishing. On fhir.org, it's used for the organization that asked for an IG to be published (each org is assigned a code e.g. argonaut). On hl7.org/fhir, it's used for realm e.g. uv or us.
The IG publisher can automatically maintain an index of IGs in this folder, or you can choose to maintain it manually (configued in publish-setup.json)
(ig)
The root folder for the IG itself. This folder contains the following important files:
- package-list.json - the list of all publications for this IG. see FHIR IG PackageList doco for documentation. This file starts empty, with only the CI-build registered
- history.html - a constructed page that presents the content of package-list.json in a human readable format
- searchform.html - a web page that allows users to search the IG - see below for documentation
- an index.html - the root page for the current milestone, or a place holder that explains that there is as yet no milestone version, with a reference to the version history
- other content as required - the content of the IG, or to support history.html
(version)
This folder contains the published version of the IG, as specially prepared by the IG publisher
the web-root will also contain a directory ig-build-zips which will contain the IG source it was built from, along with the build log. These are useful for retrospective troubleshooting.
Redirections
What's going on with all the redirections? Well, each IG as published is an instance of a FHIR Server. e.g. the canonical URL is the root URL for the server, and the server supports the READ operation on all the resources that are part of the IG - whether examples, profiles, etc. Typically a resource (Type)/(id) will be published at (type)-(id).(x) where .x is a set of files - html, xml, json, etc. (the exact details are template dependent). so (canonica)/(type)-(id).html is the page at which the resource is found. The redirections pick up a web request for (canonical)/(Type)/(id) and redirect it to the right place (the IG publisher build these redirections based on it's knowledge of how the IG was built in the first place). Why do it by redirects? because the script that does the redirect can look at the accept header, and redirect to a json/xml/turtle file if that's what the client asked for - and that's how the published IG is an instance of the specification.
The redirection files that are generated are active content, and specific to the host software that publishes the IG. The following server software is supported:
- IIS
- Apache
- Nginx
- Lightspeed
note that his is a work in progress - the IG doesn't yet publish a capability statement describing the IG itself, but that's intended to happen.
For XML and JSON files, we produce two variations (.xml1/.xml2 and .json1/.json2) with identical content. In the web-config files, mime types are tied to file extensions. Because there are different mime types that can come back depending on what version of FHIR someone is accessing the files with, we need multiple extensions. Specifically - application/xml is tied to ".xml", application/xml+fhir is tied to ".xml1", and application/fhir+xml is tied to ".xml2". The equivalent is true for JSON. This ensures that the mime type that comes back is the one that was requested. The server hosting the website will need to be configured to have those mappings from file extension to mime type.
Publishing an IG
The process for publishing an IG roughly looks like this:
- Ensure that the approvals processes / QA processes are complete (will be organization specific), and the version that will be published is agreed
- Check that the local build still meets the QA processes (changes in permissions / network infrastructure / terminology releases occasionally mean that new errors are introduced)
- Setup or Synchronize a local copy of the web-root from the server, and then make a back up local copy of it
- run the IG publisher in a special mode that builds the IG and merges it into the web-root directory, and updates all the files that might need changing (could be 100000+ files for mature publications). See below for how to run this (e.g. what parameters you need)
- Check that it published with no errors. In the case of errors, restore your local copy of web-root before re-running
- Once the process completes with no errors, copy all modified files back to the server (and use something like beyond compare, so that files that are deleted locally are also deleted on the server)
The full HL7 process, with specific details, is found at HL7 Process for Publishing a FHIR IG - this might be useful as an organizational template..
The kind of things that the IG publisher does during this publication include (but are not limited to):
- create a new (version) folder
- copy in the specially built IG (it's a little different internally, because the IG publisher now knows exactly where it's going to live on the web, and what server will host it. none of the changes are visible to human readers of the specification)
- generate all the redirects as necessary
- update package-list.json, history.html, package-feed, publication-feed, organizational indexes
- if it's a milestone release:
- delete any content in the (ig) folder that isn't accounted for by the files that are expected to be found in there, and copy in another specially built copy of the IG
- touch every single previously published html file to update information about the current milestone version
To publish an IG, you need to have 5 folders:
- source - the source of the IG, ready to publish, with a prepared publication-request.json (see below).
- ig-history - a copy of the template for the history page. HL7 uses the github repo at https://github.com/HL7/fhir-ig-history-template
- ig-registry - a copy of the github repo at https://github.com/FHIR/ig-registry - this should be up to date, because the IG publisher will make changes in it that you then submit as a PR against the master (registers your IG at http://www.fhir.org/guides/registry/)
- templates - a copy of a local folder (usually a clone of a GitHub) repo that contains the web site specific templates (searchform.template, header.template, preamble.template, and postamble.template, and possibly an index.template)
- temp - which temp folder to use. defaults to the system temp which works fine except on OSX (some weird problem crops up on some OSX distros that we've never been able to pin down, so always specify a temp folder on an OSX device)
In addition, of course, you need the web site where the source is going to be published to. There are two approaches
- Point the IG publisher directly at the web site. The IG publisher will download any files it needs from the website, and produce a folder of files that need to be copied up to the website
- Point the IG publisher at a local folder that contains all the files for the website. Typically, this is a GitHub repository that contains the web site source, but the IG Publisher is not concerned with the git(hub) part, it just works with the local folder
{web} is the address of either the web site (http(s)://...) or the local folder name.
Prepared Publication Request
The source folder must contain a publication-request.json. This must contain a package-id and version that matches the version of the IG, that is fully populated per the documentation for publication-request.json.
Running the build
Invoking the publisher is simple, once you have all the content lined up:
java -jar publisher.jar -go-publish -source (source) -web (web) -registry (ig-registry)/fhir-ig-list.json -history (ig-history) -templates (templates) -temp (temp)
Notes:
- publisher.jar is the standard IG publisher jar. The parameter -go-publish invokes the special mode
- The first part of the publication process is an extensive set of checks that ensure the information submitted (especially including the package-list.json entries) is consistent with what has previously been published. You can run the publication process any number of times while getting all this stuff to line up.
- The publisher currently pauses for a manual review of the content. Typically, you should open index.html in each of the folders for the IG in the nominated directory tree and check that it looks ok
- When the process is complete, you'll see "Finished Publishing". If you don't see that, something went wrong.
Documentation for publish-setup.json
The web-root folder contains publish.ini that tells the IG publisher about how the web-root folder is hosted:
{
"website" : {
"style" : "fhir.layout",
"url" : "http://fhir.org/guides",
"server" : "apache",
"org" : "FHIR Foundation",
"search-template" : "searchform.template.html",
"index-template" : "index.template"
},
"feeds" : {
"package" : "package-feed.xml",
"publication" : "publication-feed.xml"
}
}
Doco:
- style=fhir.layout - a fixed value that the IG publisher checks to make sure it's got the right file
- url= the web URL that this folder is found at
- server= the kind of server that hosts the web root. Values = asp-old, asp-new (both for IIS); apache (for apache or nginx); and litespeed
- org = text description of the publishing org (appears in the rss feeds)
- feeds.package= where to put the package feed. This should not be changed
- feeds.publication= where to put the publication feed. This should not be changed
Publication patterns
The publisher checks the package to see if it has a special pattern, otherwise the publish-setup.json tells the publisher how to structure the folders.
- Category and code - publish-setup.json must contain a layout like `"layout":{"id":"anyorg.anything.[category].[code]","canonical":"http://anywhere.org/anypath/[category]/[code]"}`
- Only code - publish.json must contain a layout like ``"layout":{"id":"anyorg.[org].[code]","canonical":"http://anywhere.org/anypath/[org]/[code]"}`` (when an [org] layer is used) or
``"layout":{"id":"anyorg.anything.[code]","canonical":"http://anywhere.org/anypath/[code]"}`` (when a category or [org] layer is not used)
- Special locations - these do not require any layout, the publisher will handle the structure automatically
- hl7.fhir.dk.[code] - http://hl7.dk/fhir/[code]
- hl7.fhir.eu.[code] - http://fhir.eu/fhir/ig/[code]
- hl7.fhir.ch.[code] - http://fhir.ch/ig/[code]
- hl7.fhir.be.[code] - https://www.ehealth.fgov.be/standards/fhir/[code] or http://hl7belgium.org/profiles/fhir/[code]
- hl7.fhir.cl.[code] - https://hl7chile.cl/fhir/ig/[code]
- hl7.fhir.uv.smart-app-launch - http://hl7.org/fhir/smart-app-launch
- hl7.xprod.[cat].[code] - http://hl7.org/xprod/ig/[cat]/[code]
- hl7.cda.[cat].[code] - http://hl7.org/cda/stds/[code]
- hl7.eu.[cat].[code] - http://hl7.eu/fhir/ig/[code]
1 Comment
John Moehrke
This is the IHE specialization of this process for IHE publications – https://github.com/IHE/supplement-template/wiki/IG-build-for-Publication