BackgroundAt Pragma we've been improving on some of our deployment practices during the past few releases. I've always been a great proponent of trying to automate as much of the deployment process as possible. One of the aspects that we improved on recently was the ability to automatically generate the Release Notes from our Team Foundation Server repository with every daily build of our software. This automation gives us quite a few benefits:
- Visibility into what's included with every build from the beginning of a release
- Ability to QA the Release Notes earlier in the development cycle as part of our daily builds
- Use of our existing TFS work items as the source for feedback, i.e. no context is lost between developers telling the technical writer what to include in the Release Notes. The technical writer only needs to check the grammar and spelling of the TFS Work Item feedback.
- No manual intervention required to get the bulk of the Release Notes document generated
- We want to be able to merge some manually authored content into the Release Notes document. Sections like What's New, Modifications and Maintenance typically contain functionality that are implemented by various TFS Work Items (Product Backlog Items, Tasks etc). So we wanted the ability to manually author these sections and merge the content with remainder of the document that is automatically generated.
- We want the ability to selectively filter out some Work Items from being included in the Release Notes document by running a custom TFS Query. An example of Work Items that we want to exclude are the internal bugs that were discovered for new functionality not yet released into production.
- We want to use MS Word to author the manual content to make use of our existing corporate stylesheets
- We want the output to be available as a PDF
- We want command line support to allow us to automate the process as part of our daily builds using TeamCity
- We want the PDF to use bookmarks to enable easy navigation between the different sections
- We want the PDF to use our corporate branding/style
The next step was to create a ReportRunner class to take these settings and generate the report in the PDF format:
Generating the PDF
On a high level, the process for generating the PDF turned out as follows:
- Create new blank PDF
- Add a front page
- Add (merge) the contents of the manual PDF into the new blank PDF whilst keeping track of the bookmarks contained within the manual document (see Requirement #6)
- Run through the list of processed work items to add the document content for them into relevant sections in the new PDF
- Re-bookmark the whole document to enable easy navigation throughout the whole PDF in support of requirement #6.
To add the front page and also include a custom footer on every page containing the build version, generation time stamp as well as page number, we created a ReleaseNotesPdfPageEvents class to implement the IPdfPageEvent interface that iTextSharp provides for executing custom logic when a PDF document is opened/closed, new paragraphs, sections are added etc. This class is then assigned to PageEvent of the PDF writer to ensure that the custom logic executes as part of the PDF generation process (see line 16).
After adding the front page, the content of the manual PDF is merged into the new document (lines 30-35). Keeping track of the bookmarks turned out to be quite an interesting exercise and is done as part of the Merge method.
After merging the existing content, we process the in-memory list of work item information by firstly grouping the Work Items based on their resolution types into sections like "How do I", "Bug Fixes" etc. (lines 38-41) and thereafter writing these sections into the document using the WriteWorkItems method:
For every new section we add an additional bookmark into the Bookmarks collection to make sure that we have a complete list of bookmarks for all the available sections in the document (see line 14). The final step in the generation process is to add these bookmarks into the new document using the CreateBookmarks method.