PowerShell: скриптуем создание readme файла с markdown разметкой для модулей PowerShell
Последнее время размещаю свои модули PowerShell на github. И возникло разумное желание сопроводить модули readme файлом. github поддерживает массу различных вариантов разметки, я выбрал наиболее простой на мой взгляд – markDown (он же поддерживается и другими публичными репозиториями, поэтому на нём и остановился).
Однако, перспектива написания readme файла для каждого модуля “руками”, а потом ещё и корректировка этих описания просто отпугнула. Решил нарисовать скрипт для создания readme.md по комментариям в модуле и описанию модуля.
Модуль генерации readme – ITG.Readme
Приведу код функции, в которой и реализовал требуемый функционал:
function Get-Readme { <# .Synopsis Генерирует readme файл с md разметкой по данным модуля и комментариям к его функциям. Файл предназначен, в частности, для размещения в репозиториях github. .Notes To-Do: - ошибка: генерирует заголовок примера, даже если примеров нет. - автоматический поиск и генерацию ссылок по переданному словарю - генерация ссылок по наименованиям других функций модуля, и других модулей, если таковые указаны - автоматическое выделение url и формирование синтаксиса ссылки в разделах Link - ввести поддержку генерации файла для внешних скриптов (именно - с генерацией файла) - а также для прочих членов модуля - about_commonparameters и другие аналогичные так же в ссылки преобразовывать .Inputs Через конвейер функция принимает описатели модулей, функций, скриптов. Именно для них и будет сгенерирован readme.md. Получены описатели могут быть через Get-Module, Get-Command и так далее. .Outputs String. Содержимое readme.md. .Link [MarkDown (md) Syntax](http://daringfireball.net/projects/markdown/syntax) .Link [about_comment_based_help](http://technet.microsoft.com/ru-ru/library/dd819489.aspx) .Link [Написание справки для командлетов](http://go.microsoft.com/fwlink/?LinkID=123415) .Example Get-Module 'ITG.Yandex.DnsServer' | Get-Readme | Out-File -Path 'readme.md' -Encoding 'UTF8' -Width 1024; Генерация readme.md файла для модуля `ITG.Yandex.DnsServer` в текущем каталоге. .Example Get-Module 'ITG.Yandex.DnsServer' | Get-Readme -OutDefaultFile; Генерация readme.md файла для модуля `ITG.Yandex.DnsServer` в каталоге модуля. #> [CmdletBinding( DefaultParametersetName='ModuleInfo' )] param ( # Описатель модуля [Parameter( Mandatory=$true , Position=0 , ValueFromPipeline=$true , ParameterSetName='ModuleInfo' )] [PSModuleInfo] [Alias('Module')] $ModuleInfo , [Parameter( ParameterSetName='ModuleInfo' )] [switch] $OutDefaultFile , # Описатель внешнего сценария [Parameter( Mandatory=$true , Position=0 , ValueFromPipeline=$true , ParameterSetName='ExternalScriptInfo' )] [System.Management.Automation.ExternalScriptInfo] $ExternalScriptInfo , # Описатель внешнего сценария [Parameter( Mandatory=$true , Position=0 , ValueFromPipeline=$true , ParameterSetName='FunctionInfo' )] [System.Management.Automation.FunctionInfo] $FunctionInfo , [switch] [Alias('Short')] $ShortDescription ) process { switch ( $PsCmdlet.ParameterSetName ) { 'ModuleInfo' { $Funcs = @( ` $ModuleInfo.ExportedFunctions.Values ` | Sort-Object -Property ` @{ Expression={ ( $_.Name -split '-' )[1] } } ` , @{ Expression={ ( $_.Name -split '-' )[0] } } ` ); $ReadMeContent = & { ` @" $($ModuleInfo.Name) $($ModuleInfo.Name -replace '.','=') $($ModuleInfo.Description) Версия модуля: **$( $ModuleInfo.Version.ToString() )** "@ $Funcs ` | Group-Object -Property ` @{ Expression={ ( $_.Name -split '-' )[1] } } ` | % -Begin { @" Функции модуля -------------- "@ } ` -Process { @" ### $($_.Name) "@ $_.Group ` | Get-Readme -ShortDescription ` ; }; if ( -not $ShortDescription ) { @" Подробное описание функций модуля --------------------------------- "@ $Funcs ` | Get-Readme ` ; }; }; if ( $OutDefaultFile ) { $ReadMeContent ` | Out-File ` -FilePath ( Join-Path ` -Path ( Split-Path -Path ( $ModuleInfo.Path ) -Parent ) ` -ChildPath 'readme.md' ` ) ` -Force ` -Encoding 'UTF8' ` -Width 1024 ` ; } else { return $ReadMeContent; }; } 'ExternalScriptInfo' { } 'FunctionInfo' { $Help = ( $FunctionInfo | Get-Help -Full ); $Syntax = ( $Help.Syntax.SyntaxItem ` | % { ,$_.Name ` + ( $_.Parameter ` | % { #MamlCommandHelpInfo#parameter $name="-$($_.Name)"; if ( $_.position -ne 'named' ) { $name="[$name]"; }; if ( $_.parameterValue ) { $param = "$name <$($_.parameterValue)>"; } else { $param = "$name"; }; if ( $_.required -ne 'true' ) { $param = "[$param]"; }; $param; } ) ` + ( & { if ( $FunctionInfo.CmdletBinding ) { '<CommonParameters>' } } ) ` -join ' ' } ); @" #### $($FunctionInfo.Name) "@ if ( $ShortDescription ) { $Help.Synopsis; $Syntax ` | % { @" $_ "@ }; } else { if ( $Help.Description ) { $Help.Description; } else { $Help.Synopsis; }; @" ##### Синтаксис "@ $Syntax ` | % { @" $_ "@ }; if ( $Help.Component ) { @" ##### Компонент $($Help.Component) "@ }; if ( $Help.Functionality ) { @" ##### Функциональность Описываемая функция предоставляет следующую функциональность: $($Help.Functionality). "@ }; if ( $Help.Role ) { @" ##### Требуемая роль пользователя Для выполнения функции $($FunctionInfo.Name) требуется роль $($Help.Component) для учётной записи, от имени которой будет выполнена описываемая функция. "@ }; if ( $Help.Inputs ) { @" ##### Принимаемые данные по конвейеру $($Help.Inputs) "@ }; if ( $Help.Outputs ) { @" ##### Передаваемые по конвейеру данные $($Help.Outputs) "@ }; if ( $Help.Parameters.parameter.Count ) { $ParamsDescription = ` ( $Help.Parameters | Out-String ) ` -replace '<CommonParameters>', '-<CommonParameters>' ` -replace '(?m)^\p{Z}{4}-(.+)?\s*?$', '- `$1`' ` ; @" ##### Параметры $ParamsDescription "@ }; if ( ( @( $Help.examples ) ).count ) { $Help.Examples.example ` | % -Begin { $ExNum=0; @" ##### Примеры использования "@ } ` -Process { ++$ExNum; $Comment = ( ( $_.remarks ` | % { $_.Text } ) -join ' ' ).Trim( ' ', (("`t").Normalize()) ); if ( $Comment ) { @" $ExNum. $Comment "@ } else { @" $ExNum. Пример $ExNum. "@ }; @" $($_.code) "@ }; }; $links = ` $Help.relatedLinks.navigationLink ` | ? { $_.LinkText } ` ; if ( $links ) { @" ##### Связанные ссылки "@ $links ` | % { @" - $($_.LinkText) "@ }; }; }; }; }; } } Export-ModuleMember ` Get-Readme ` ;
Да, так вот смешно её и назвал – Get-Readme
. Данная функция на вход (через конвейер) может принимать описатели модулей, функций, внешних скриптов (ну не придумал я иного перевода к external script). Правда, пока для external script она ничего генерировать не будет, ещё не дописал, а для модулей и функций – вполне работоспособна. Ну и в случае генерации readme для модуля поддерживает ещё один параметр – OutDefaultFile
(флаг). При установке этого флага readme не будет отправлен по конвейеру, а будет записан в файл readme.md в каталоге модуля, описатель которого был передан функции Get-Readme.
Пример использования
Import-Module -Path 'ITG.Yandex' -PassThru ` | Get-Readme -OutDefaultFile;
Эти несколько строк загрузят модуль ITG.Yandex, и будет создан файл readme.md в каталоге модуля ITG.Yandex.
Что же мы получим в итоге? На примере модуля ITG.Yandex сам текст readme.md можно подсмотреть на github, а результат генерации html по markdown размере этого файла – можно увидеть здесь. Согласитесь, с таким readme куда проще знакомиться с модулем, чем вовсе без readme.
Как это работает?
Для генерации описания я использовал механизмы powershell. В частности – возможность генерации описания специальным образом оформленных комментариев. А описание и версию самого модуля так же получаю через механизмы powerShell. Для того, чтобы снабдить Ваш модуль необходимой информацией, необходимо создать к нему файл манифеста модуля. Для модуля ITG.Readme файл манифеста ITG.Readme.psd1 выглядит следующим образом:
# # Манифест модуля для модуля "ITG.Readme". # # Создано: Sergey S. Betke # # Дата создания: 13.11.2012 # # Архив проекта: https://github.com/sergey-s-betke/ITG.Readme # @{ # Файл модуля скрипта или двоичного модуля, связанный с данным манифестом ModuleToProcess = 'ITG.Readme.psm1' # Номер версии данного модуля. ModuleVersion = '1.3.6' # Уникальный идентификатор данного модуля GUID = '826e836c-d10c-4d4d-b86b-8b4a41829b00' # Автор данного модуля Author = 'Sergey S. Betke' # Компания, создавшая данный модуль, или его поставщик CompanyName = 'IT-Service.Nov.RU' # Заявление об авторских правах на модуль Copyright = '(c) 2012 Sergey S. Betke. All rights reserved.' # Описание функций данного модуля Description = 'Набор функций для PowerShell для генерации readme файла для модулей и функций.' # Минимальный номер версии обработчика Windows PowerShell, необходимой для работы данного модуля PowerShellVersion = '2.0' # Имя узла Windows PowerShell, необходимого для работы данного модуля PowerShellHostName = '' # Минимальный номер версии узла Windows PowerShell, необходимой для работы данного модуля PowerShellHostVersion = '' # Минимальный номер версии компонента .NET Framework, необходимой для данного модуля DotNetFrameworkVersion = '2.0' # Минимальный номер версии среды CLR (общеязыковой среды выполнения), необходимой для работы данного модуля CLRVersion = '2.0' # Архитектура процессора (нет, X86, AMD64, IA64), необходимая для работы модуля ProcessorArchitecture = '' # Модули, которые необходимо импортировать в глобальную среду перед импортированием данного модуля RequiredModules = @( ) # Сборки, которые должны быть загружены перед импортированием данного модуля RequiredAssemblies = @() # Файлы скрипта (.ps1), которые запускаются в среде вызывающей стороны перед импортированием данного модуля ScriptsToProcess = @( ) # Файлы типа (.ps1xml), которые загружаются при импорте данного модуля TypesToProcess = @() # Файлы формата (PS1XML-файлы), которые загружаются при импорте данного модуля FormatsToProcess = @() # Модули для импортирования в модуль, указанный в параметре ModuleToProcess, в качестве вложенных модулей NestedModules = @() # Функции для экспорта из данного модуля FunctionsToExport = '*' # Командлеты для экспорта из данного модуля CmdletsToExport = '*' # Переменные для экспорта из данного модуля VariablesToExport = '*' # Псевдонимы для экспорта из данного модуля AliasesToExport = '*' # Список всех модулей, входящих в пакет данного модуля ModuleList = @() # Список всех файлов, входящих в пакет данного модуля FileList = ` 'ITG.Readme.psm1' # Личные данные, передаваемые в модуль, указанный в параметре ModuleToProcess PrivateData = '' }
Описание модуля я беру из параметра Description
(его значение вполне может быть многострочным, не скупитесь на подробности), версию – из ModuleVersion
. Что же касается описания функций – для этого и существуют специально подготовленные комментарии. Пример таких комментариев как раз и представлен в заголовке функции Get-Readme в начале статьи. Подобные комментарии, как Вы понимаете, позволяют не только readme генерировать. В первую очередь они позволят Вам использовать следующий код:
Get-Help Get-Readme -Details;
Другими словами, настоятельно рекомендую писать “правильные” комментарии к собственным функциям. Массу приятных бонусов в результате можно получить, в том числе – и readme .
Где это забрать?
Репозиторий проекта ITG.Readme размещён на github. Пользуйтесь на здоровье .
Отзывы » (1)
RSS комментарии
Обратная ссылка
Ещё один репозиторий на github открыл — http://github.com/IT-Service/ITG.Readme. Уже версия 1.5.1. Все пожелания к модулю лучше писать в багтрекер — http://github.com/it-service/ITG.Readme/issues.