:toc: macro :toclevels: 5 :figure-caption!: :ascii_doc_link: link:https://asciidoctor.org/docs/what-is-asciidoc[ASCII Doc] :ascii_doctor_link: link:https://asciidoctor.org[Asciidoctor] :cff_link: link:https://github.com/citation-file-format/ruby-cff[CFF] :etcher_link: link:https://alchemists.io/projects/etcher[Etcher] :firefox_link: link:https://www.mozilla.org/en-US/firefox[Firefox] :gem_specification_link: link:https://guides.rubygems.org/specification-reference[Gem Specification] :gemsmith_link: link:https://alchemists.io/projects/gemsmith[Gemsmith] :git_commit_anatomy_link: link:https://alchemists.io/articles/git_commit_anatomy[Git Commit Anatomy] :git_link: link:https://git-scm.com[Git] :git_lint_link: link:https://alchemists.io/projects/git-lint[Git Lint] :git_notes_link: link:https://alchemists.io/articles/git_notes[Git Notes] :git_trailers_link: link:https://alchemists.io/articles/git_trailers[Git Trailers] :hanami_link: link:https://hanamirb.org[Hanami] :hanami_views_link: link:https://alchemists.io/articles/hanami_views[Hanami Views] :hanamismith_link: link:https://alchemists.io/projects/hanamismith[Hanamismith] :iterm_link: link:https://iterm2.com[iTerm 2] :lode_link: link:https://alchemists.io/projects/lode[Lode] :markdown_link: link:https://daringfireball.net/projects/markdown[Markdown] :marked_link: link:https://marked2app.com[Marked 2] :net_news_wire_link: link:https://netnewswire.com[NetNewsWire] :redcarpet_link: link:https://github.com/vmg/redcarpet[Redcarpet] :rouge_link: link:https://rouge.jneen.net[Rouge] :ruby_link: link:https://www.ruby-lang.org[Ruby] :rubygems_link: link:https://rubygems.org[RubyGems] :rubysmith_link: link:https://alchemists.io/projects/rubysmith[Rubysmith] :runcom_link: link:https://alchemists.io/projects/runcom[Runcom] :sanitize_link: link:https://github.com/rgrove/sanitize[Sanitize] :strict_semantic_versioning_link: link:https://alchemists.io/articles/strict_semantic_versioning[Strict Semantic Versioning] :string_formats_link: link:https://docs.ruby-lang.org/en/master/format_specifications_rdoc.html[String Formats] :syndication_link: link:https://alchemists.io/articles/syndication[Syndication] :versionaire_link: link:https://alchemists.io/projects/versionaire[Versionaire] :xdg_link: link:https://alchemists.io/projects/xdg[XDG] = Milestoner Milestoner is a pure {git_link} based Command Line Interface (CLI) for software deployment automation that is agnostic of the programming language or source control service you use (i.e. GitHub, GitLab, Bitbucket, etc). Each milestone (i.e. {git_link} tag) includes the automatic generation of feature rich release notes sourced from the {git_link} commits that make up the tag. Finally, each milestone adheres to {strict_semantic_versioning_link} so your milestones remain consistent. You can use Milestoner to inspect the current state of the next milestone, celebrate the team members that contribute to each milestone, and share information with stakeholders with minimal effort. By having a tool, like Milestoner, you can automate software releases in a consistent and reliable fashion. 🎉 Milestoner pairs well with the following gems: * {git_lint_link}: Lints each {git_link} commit to ensure they are of high quality for readability, historic inspection, and release note generation. * {rubysmith_link}: Automates building new {ruby_link} projects. * {gemsmith_link}: Automates building new {rubygems_link} projects. * {hanamismith_link}: Automates building new {hanami_link} projects. toc::[] == Screenshots The following are screenshots of using this gem to render release notes for the {lode_link} gem. Even though {lode_link} is used for example purposes, keep in mind that Milestoner can be used for _any project and/or programming language_. === ASCII Doc image:https://alchemists.io/images/projects/milestoner/screenshots/build-ascii_doc-collapsed.png[Release Notes,width=1149,height=830,role=focal_point] image:https://alchemists.io/images/projects/milestoner/screenshots/build-ascii_doc-expanded.png[Release Notes,width=1149,height=1054,role=focal_point] * *Command*: `milestoner build --format ascii_doc` * *Renderer*: {ascii_doc_link} === Feed image:https://alchemists.io/images/projects/milestoner/screenshots/build-feed-collapsed.png[Release Notes,width=1109,height=1143,role=focal_point] image:https://alchemists.io/images/projects/milestoner/screenshots/build-feed-expanded.png[Release Notes,width=1109,height=1143,role=focal_point] * *Command*: `milestoner build --format feed` * *Renderer*: {net_news_wire_link} === Markdown Format image:https://alchemists.io/images/projects/milestoner/screenshots/build-markdown.png[Release Notes,width=789,height=718,role=focal_point] * *Command*: `milestoner build --format markdown` * *Renderer*: {marked_link} === Stream image:https://alchemists.io/images/projects/milestoner/screenshots/build-stream.png[Release Notes,width=586,height=341,role=focal_point] * *Command*: `milestoner build --format stream` * *Renderer*: {iterm_link} === Web image:https://alchemists.io/images/projects/milestoner/screenshots/build-web-collapsed.png[Release Notes,width=1195,height=878,role=focal_point] image:https://alchemists.io/images/projects/milestoner/screenshots/build-web-expanded.png[Release Notes,width=1195,height=878,role=focal_point] * *Command*: `milestoner build --format web` * *Renderer*: {firefox_link} == Features * Uses {versionaire_link} for {strict_semantic_versioning_link}. Example: ** Format: `+..+`. ** Example: `0.0.0`. * Defaults to including {git_link} commits since last tag (or initialization of repository) when building release notes. This can be customized further if desired. * Ensures {git_link} commit messages are grouped by prefix, in order defined, for categorization. For more details, see link:https://alchemists.io/projects/git-lint/#_commit_subject_prefix[Git Lint Commit Subject Prefix]. Defaults (can be customized): ** image:https://alchemists.io/images/projects/milestoner/icons/fixed.png[Fixed] Fixed ** image:https://alchemists.io/images/projects/milestoner/icons/added.png[Added] Added ** image:https://alchemists.io/images/projects/milestoner/icons/updated.png[Updated] Updated ** image:https://alchemists.io/images/projects/milestoner/icons/removed.png[Removed] Removed ** image:https://alchemists.io/images/projects/milestoner/icons/refactored.png[Refactored] Refactored * Ensures {git_link} commit messages are alphabetically sorted for release note categorization and readability. * Provides automatic versioning based on last {git_link} tag and {git_trailers_link}. See {git_commit_anatomy_link} for details. * Supports multiple build formats: ** {ascii_doc_link} ** {syndication_link} ** {markdown_link} ** Stream (console) ** link:https://html.spec.whatwg.org/multipage[Web] * Supports {git_notes_link}. * Supports customization via your personal {xdg_link}, {runcom_link}, and/or {hanami_views_link} configuration. == Requirements . A UNIX-based system. . https://www.ruby-lang.org[Ruby]. . https://www.gnupg.org[GnuPG] (optional). == Setup To install _with_ security, run: [source,bash] ---- # 💡 Skip this line if you already have the public certificate installed. gem cert --add <(curl --compressed --location https://alchemists.io/gems.pem) gem install milestoner --trust-policy HighSecurity ---- To install _without_ security, run: [source,bash] ---- gem install milestoner ---- == Usage === Command Line Interface (CLI) From the command line, type: `milestoner --help` image:https://alchemists.io/images/projects/milestoner/screenshots/usage.png[Usage,width=670,height=412,role=focal_point] === Customization This gem can be configured via a global configuration: .... ~/.config/milestoner/configuration.yml .... It can also be configured via {xdg_link} environment variables. The default configuration is: [source,yaml] ---- avatar: uri: "https://avatars.githubusercontent.com/u/%s" build: basename: "index" format: "stream" layout: "page" max: 1 root: "tmp/milestones" stylesheet: "page" tail: "head" commit: categories: - emoji: "✅" label: "Fixed" - emoji: "🟢" label: "Added" - emoji: "🔼" label: "Updated" - emoji: "⛔️" label: "Removed" - emoji: "🔁" label: "Refactored" format: "asciidoc" uri: "https://github.com/%s/%s/commit/%s" organization: label: "Undefined" uri: "https://undefined.io" profile: uri: "https://github.com/%s" project: owner: "undefined" uri: home: "%s/projects/%s" version: "%s/versions/%s" review: uri: "https://github.com/%s/%s/pulls/%s" syndication: categories: - label: "Milestones" name: "milestones" entry: label: "%s" uri: "%s" id: "%s" label: "%s: %s" links: - label: "%s: %s (web)" mime: "text/html" relation: "alternate" uri: "%s/versions" - label: "%s: %s (feed)" mime: "application/atom+xml" relation: "self" uri: "%s/versions.xml" tracker: uri: "https://github.com/%s/%s/issues/%s" ---- The above can be customized as follows: * `avatar`: Manages team member avatar details. ** `uri`: Required. The URI format for linking to avatars as formatted using {string_formats_link}. Default: GitHub. The `id` is dynamically calculated via the `external_id` of the user stored in the {lode_link} cache. * `build`: Manages build details. ** `basename`: Required. The build file basename. Default: `index`. Used to customize the built file name. ** `format`: Required. The build output format. Default: `stream`. Used to determine what format to build the release notes as. Multiple formats are supported. ** `layout`: Required. The {hanami_views_link} layout used when building release notes. Default: page. This can be disabled when using `false` or customized further -- via your own {xdg_link} configuration -- when providing your own templates and/or partials. ** `max`: Required. The maximum number of {git_link} tags to build. Default: 1. By default, you are limited to building release notes for changes (commits) since the last tag but you can increase the maximum to build release notes for as many tags as you like. ** `root`: Required. The the root build directory. Default: `tmp/milestones`. This can be a relative or absolute path and defaults your current project. The path is automatically created if missing. ** `stylesheet`: Required. The stylesheet basename which is only used by the _web_ format. Default: `page`. This can be a relative path (i.e. `../page`) which is especially handy when build `max` is set to a number greater than one so the release notes for each tag you build loads the stylesheet properly. The path is automatically created if missing. * `commit`: Manages commit categories, emojis, hyperlinks, and formatting. ** `categories`: Required. Defaults to five categories which pairs well with the {git_lint_link} gem. Category order is important with the first taking precedence over the second and so forth. Special characters are allowed for prefixes but should be enclosed in quotes. To disable categories, use an empty array. Example: `categories: []`. *** `emoji`: Required. The emoji associated with the label for output purposes. _Used by the {ascii_doc_link}, {markdown_link}, and stream build formats_. Defaults to the provided emojis. *** `label`: Required. Allows you to customize the category label. All commits are grouped by label which equates to the prefix, or first word, used in each commit message. The defaults pair well with the {git_lint_link} gem. Defaults to the provided labels. ** `format`: Required. Defines the default format used for rendering commit messages unless specified in the commit trailer metadata which takes higher precedence. Default: `asciidoc`. ** `uri`: Required. The URI for linking to commits as formatted using {string_formats_link}. Default: GitHub. The `id` is dynamically calculated via the commit SHA of each commit analyzed at runtime. * `generator`: Manages generator details. ** `label`: Required. The label of the generator used for all software milestones. Default: Milestoner. ** `uri`: Required. The URI of the generator used for all software milestones. Defaults to Milestoner's homepage URL as provided by the {gem_specification_link} of this project. ** `version`: Required. The version of the generator used for all software milestones. Defaults to Milestoner's current version as provided by the {gem_specification_link} of this project. * `loaded_at`: Required. Dynamically calculated when the configuration is loaded and is generally meant to represent current time. You can customize this value but is not recommended. * `organization`: Manages organization details. ** `label`: Required. The organization's label. Can be used within other keys via {string_formats_link} and is meant for branding purposes. ** `uri`: Required. The organization's home page URI. Can be used within other keys via {string_formats_link}. * `profile`: Manages team member profile details. ** `uri`: Required. The URI format for linking to profiles as formatted using {string_formats_link}. Default: GitHub. The `id` is dynamically calculated via the `handle` of the user stored in the {lode_link} cache. * `project`: Manages project details. ** `author`: Required. The project author. Dynamically calculated by the {etcher_link} gem in the following order: This value or {git_link} configuration user name. ** `description`: Optional. The project description. Dynamically calculated by the {etcher_link} gem in the following order: This value, {gem_specification_link} summary, or {cff_link} abstract. ** `label`: Optional. The project label. Dynamically calculated by the {etcher_link} gem in the following order: This value, {gem_specification_link} metadata label, or {cff_link} title. ** `name`: Required. The project name. Dynamically calculated by the {etcher_link} gem in the following order: This value or {gem_specification_link} name. ** `owner`: Required. The project owner. This is your source code organization or user handle. Used when formatting URLs (mentioned above). Default: `undefined`. It is strongly recommended you configure this value so all links are formatted properly. ** `uri`: Manages project URI details. *** `home`: Required. The project home URI. Dynamically calculated by the {etcher_link} gem in the following order: This value, {gem_specification_link} homepage, or {cff_link} URL. *** `icon`: Optional. The project icon URI. Used for branding. For example, you could use an organization specific URI: `"%s/images/projects/%s/favicon.ico"`. *** `logo`: Optional. The project logo URI. Used for branding. For example, you could use an organization specific URI: `"%s/images/projects/%s/logo.png"`. *** `version`: Required. The project version URI. Defaults to the versions folder of your project home URI. This ensures all release notes link back to your project. ** `version`: Required. The project version. Dynamically calculated based on the last {git_link} tag of your project and {git_link} `Milestone` commit trailer metadata. The default is: `0.0.0`. For more on this see, the _Automatic Versioning_ section below. You can configure a value but is _not recommended_ since any custom value you supply will be used for _all_ deployments and release notes. You're better off letting this gem compute this for you. * `review`: Manages code review details. ** `uri`: Required. The URI format for linking to code reviews as formatted using {string_formats_link}. Default: GitHub. The `id` is currently a _placeholder_ for future feature support when API support is added. For now this links to _all_ code reviews with the goal to link to individual code reviews based on {git_trailers_link}. * `syndication`: Manages syndicated feed details when used with the `feed` build format. ** `categories`: Required. Manages category details. *** `label`: Required. The category label. Default: Milestones. *** `name`: Required. The category name. Default: milestone. ** `entry`: Required. Manages feed entry details which are the details of each {git_link} tag. *** `label`: Required. The entry label. Default: `%s`. Automatically calculated, at runtime, for the current version. *** `uri`: Required. The entry URI. The full URI to your project version. The default uses your project version URI. If customized, ensure you include `%s` so the URI can properly link to the calculated version at runtime. ** `id`: The ID of your feed which, per Atom specification, should be the the URI of your project (including version) which is why this defaults to your project version URI. If customized, ensure the `%s` is included for proper runtime calculation. ** `label`: The label of your feed and is what people will see when subscribing to your feed. Defaults to dynamic string formatting based on existing configuration values. ** `links`: Required. Provides links to HTML and XML versions of your feed. This can be an array of links but generally you only need HTML and XML formats. *** `label`: Required. The link label. Defaults to dynamic string formatting based on existing configuration values. *** `mime`: Required. The mime type. Defaults to HTML and XML. *** `relation`: Required. Identifies the relation of the link which can either be `self` (i.e. XML) or `alternate` (i.e. HTML). *** `uri`: Required. The link URI to follow for more information. Defaults to dynamic string formatting based on existing configuration values. * `tracker`: Required. Managed issue tracker details. ** `uri`: Required. The URI format for linking to issues as formatted using {string_formats_link}. Default: GitHub. The `id` is dynamically calculated via the commit `Issue` trailer as linted by {git_lint_link}. When no ID can be found, this will default to _All_ issues. As hinted at above, all URIs (including syndication), support {string_formats_link}. This means you can use the `%s` format -- replacing `key` with the key of your choice -- to customize your configuration further. 💡 If you need to know what your current configuration looks like, you can jump into your applications IRB console and inspect `Milestoner::Container[:settings]` to see full details. === Config image:https://alchemists.io/images/projects/milestoner/screenshots/usage-config.png[Usage,width=632,height=352,role=focal_point] Milestoner can be configured via the command line using: `milestoner config`. This allows you to create, edit, view, and/or delete your global or local configuration as desired. The configuration is managed by the {runcom_link} gem which is built atop the {xdg_link} gem for managing global or local configurations. Please read the documentation of each gem to learn more. === Cache image:https://alchemists.io/images/projects/milestoner/screenshots/usage-cache.png[Usage,width=625,height=318,role=focal_point] Milestoner's cache allows you to enrich user information (i.e. authors, collaborators, etc) by storing information in a `PStore` database as managed by the {lode_link} gem. Cache location, as with the Config, is managed by the {runcom_link} gem. User information should be sourced from whatever service you use for managing your source code. For example, when using GitHub, your workflow might look like this: [source,bash] ---- milestoner cache --list # 🟢 [milestoner] Listing users... # 🟢 [milestoner] No users found. milestoner cache --create "111,jsmith,Jane Smith" # 🟢 [milestoner] Created: "Jane Smith" milestoner cache --create "222,jdoe,John Doe" # 🟢 [milestoner] Created: "John Doe" milestoner cache --create "333,jgrey,Jill Grey" # 🟢 [milestoner] Created: "Jill Grey" milestoner cache --list # 🟢 [milestoner] Listing users... # External ID, Handle, Name # ------------------------- # "111", "jsmith", "Jane Smith" # "222", "jdoe", "John Doe" # "333", "jgrey", "Jill Grey" milestoner cache --delete "Jill Grey" # 🟢 [milestoner] Deleted: "Jill Grey". milestoner cache --list # 🟢 [milestoner] Listing users... # External ID, Handle, Name # ------------------------- # "111", "jsmith", "Jane Smith" # "222", "jdoe", "John Doe" milestoner cache --info # 🟢 [milestoner] Path: /Users/bkuhlmann/.cache/milestoner/database.store. ---- 💡 Use `+https://api.github.com/users/+` to acquire the external ID for any GitHub user. Once team member information is stored in your cache, you'll be able to build release notes which automatically link to GitHub user information without constantly hitting the GitHub API. _Users are identified by name so the full author name used for each commit message needs to match the same user name as stored in your source repository hosting service._ If you don't use the cache, your release notes use a question mark (?) and _unknown_ for team members as highlighted below: image:https://alchemists.io/images/projects/milestoner/screenshots/no_cache.png[Usage,width=916,height=397,role=focal_point] === Build image:https://alchemists.io/images/projects/milestoner/screenshots/usage-build.png[Usage,width=679,height=437,role=focal_point] The build command allows you to quickly build release notes to check the current status of your project or deploy a new milestone. By default, the build command uses either the default or custom configuration as documented in the _Configuration_ section above. This means, when using the defaults, you can immediately build the release notes for your project in a temporary directory: [source,bash] ---- milestoner build --format web # 🟢 [milestoner] Building Milestoner (web)... # 🟢 [milestoner] Built: /Users/bkuhlmann/Engineering/OSS/milestoner/tmp/milestones/page.css. # 🟢 [milestoner] Built: /Users/bkuhlmann/Engineering/OSS/milestoner/tmp/milestones/index.html. ---- The above command is so useful that I use the following `msw` (i.e. Milestoner Web) Bash alias to build current release notes or release notes for several tags: [source,bash] ---- # Label: Milestoner (web) # Description: Build milestone(s) in web format. # Parameters: $1 (optional): Maximum tags to build. Default: 1. msw() { local max=${1:-1} local root="tmp/milestones" local path="$root/index.html" rm -rf tmp/milestones if [[ "$max" == 1 ]]; then milestoner build --max "$max" --format web if [[ -f "$path" ]]; then open "$path" fi else milestoner build --max "$max" --stylesheet "../page" --format web if [[ -d "$root" ]]; then ruby -run -e httpd "tmp/milestones" --port 3030 & open "http://localhost:3030" fg fi fi } ---- Check out the help documentation (i.e. `milestoner build --help`) for addition usage that explains what command line options you can use to overwrite the current configuration. ==== Commits By default, all {git_link} commit messages support {ascii_doc_link} syntax but you can use {markdown_link} if customized. {ascii_doc_link} is rendered using the {ascii_doctor_link} gem while {markdown_link} is rendered using the {redcarpet_link} gem. Regardless of what renderer you choose, each supports syntax highlighting via the {rouge_link} gem. This also means you can customize the {rouge_style} stylesheet as documented in the xref:_templates[Templates] section below. Here's a couple examples of commit messages using {ascii_doc_link} and {markdown_link} syntax: *ASCII Doc* image:https://alchemists.io/images/projects/milestoner/screenshots/syntax-ascii_doc.png[ASCII Doc,width=950,height=763,role=focal_point] *Markdown* image:https://alchemists.io/images/projects/milestoner/screenshots/syntax-markdown.png[Markdown,width=933,height=921,role=focal_point] 💡 To see an example of what this renders to HTML as, see the xref:_formats[Formats] section below. ==== Sanitization Sanitization of commit messages is handled by the {sanitize_link} gem. This means you can only use a limited set of HTML elements (this includes {ascii_doc_link} and {markdown_link} rendering). Here's what's allowed: * link:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/b[b] * link:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/em[em] * link:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/i[i] * link:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/strong[strong] * link:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/u[u] * link:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a[a] * link:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/abbr[abbr] * link:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/blockquote[blockquote] * link:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/br[br] * link:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/cite[cite] * link:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/code[code] * link:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dd[dd] * link:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dfn[dfn] * link:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dl[dl] * link:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dt[dt] * link:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/kbd[kbd] * link:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/li[li] * link:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/mark[mark] * link:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ol[ol] * link:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/p[p] * link:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/pre[pre] * link:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/q[q] * link:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/s[s] * link:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/samp[samp] * link:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/small[small] * link:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/strike[strike] * link:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/sub[sub] * link:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/sup[sup] * link:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/time[time] * link:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ul[ul] * link:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/var[var] * link:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/audio[audio] * link:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details[details] * link:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img[img] * link:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/source[source] * link:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/span[span] * link:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/summary[summary] * link:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video[video] The following global attributes are allowed for all elements: * link:https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/id[id] * link:https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/class[class] The following attributes are limited to only a few elements like `a`, `abbr`, and `dfn` for the most part. * link:https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/title[title] Other attributes are allowed as well but are specific to each element. Ultimately, if you don't see an attribute being rendered then it's because it's not allowed. ==== Trailers Multiple {git_trailers_link} for your commits are supported which are detailed in the linked article. At a minimum, the `Milestone` trailer is highly recommended since this is how Milestoner handles xref:_automatic_versioning[Automatic Versioning] for you. One of the more powerful features of using {git_trailers_link} in your commit messages is they give you the ability to fully resolve what is defined in your default global xref:_customization[configuration]. Here's a more detailed breakdown: * *Format* (optional): Use `ascii_doc` or `markdown` for the value to control what syntax used to render your commit message. The default is `ascii_doc` but if your configuration uses a different default you can override that per commit message if desired. * *Issue* (optional): When supplied, Milestoner will automatically associate your commit with the corresponding issue ID you provide as a value. This works in conjunction with your xref:_customization[configuration]. * *Milestone* (optional): This is detailed in the xref:_automatic_versioning[Automatic Versioning] section below. ==== Automatic Versioning As mentioned earlier, the calculation of version information happens automatically based on your last {git_link} tag and any {git_link} commit trailer metadata used. If none of this information is present, then the default version of `0.0.0` is used instead. All of this information is available to you via the following command: [source,bash] ---- milestoner build --help ---- Running the above will dynamically show you latest version information -- along with help documentation -- in case you have doubts. You can use this as a status check as well. If you don't want to use the automatic version, you can override by using the `--version` option when building. Example: [source,bash] ---- # Uses automatic version. milestoner build --format stream # Uses manual version. milestoner build --format stream --version 1.2.3 ---- By default, automatic versioning is based on your last known {git_link} tag. The version is bumped based on {git_link} commit trailer information from untagged commits (i.e. commits created since the last tag). All of this is managed via the {versionaire_link} gem. To ensure automatic versioning works properly, you only need to add the `Milestone` {git_link} commit trailer with a value of: `patch`, `minor`, or `major`. Here's an example assuming you have published Version 1.0.0: .... # First commit. Milestone: patch # Second commit. Milestone: minor # Third commit Milestone: patch .... Given the above, the resulting version would be: 1.1.0. This is because the highest milestone was a _minor_ milestone. The highest milestone wins and doesn't matter how many commits you made with the same milestone trailer information or the order in which the commits were made. Here's another example: .... # First commit. Milestone: patch # Second commit. Milestone: patch # Third commit Milestone: patch .... Given the above, the resulting version would be: 1.0.1. This is because the highest milestone was a _patch_. Here's a final example: .... # First commit. Milestone: major # Second commit. Milestone: minor # Third commit Milestone: patch .... Given the above, the resulting version would be: 2.0.0. This is because the highest milestone was a _major_ milestone. ==== Templates Build template functionality is powered by {hanami_views_link} which means you can completely customize _all_ build formats, templates, partials, stylesheets, images, and so much more. The quickest way to start customization is to copy the `templates` folder structure -- included with this gem -- to your preferred {runcom_link} configuration (i.e. global or local). For example, this gem's template structure is: .... lib/milestoner/templates ├── layouts │ ├── page.adoc.erb │ ├── page.html.erb │ ├── page.md.erb │ ├── page.stream.erb │ └── page.xml.erb ├── milestones │ ├── _avatar.adoc.erb │ ├── _avatar.html.erb │ ├── _avatar.md.erb │ ├── _avatar.xml.erb │ ├── _commit.adoc.erb │ ├── _commit.html.erb │ ├── _commit.md.erb │ ├── _commit.stream.erb │ ├── _commit.xml.erb │ ├── _icon.adoc.erb │ ├── _icon.html.erb │ ├── _icon.md.erb │ ├── _icon.xml.erb │ ├── _profile.adoc.erb │ ├── _profile.html.erb │ ├── _profile.md.erb │ ├── _profile.xml.erb │ ├── _tag.adoc.erb │ ├── _tag.html.erb │ ├── _tag.md.erb │ ├── _tag.stream.erb │ ├── _tag.xml.erb │ ├── show.adoc.erb │ ├── show.html.erb │ ├── show.md.erb │ ├── show.stream.erb │ └── show.xml.erb └── public └── page.css.erb .... This means you could, for example, copy all of Milestoner's default templates to your own {runcom_link} configuration and customize as you see fit. Example (using global configuration): [source,bash] ---- cp -r /lib/milestoner/templates $HOME/.config/milestoner/templates ---- Milestoner searches your {runcom_link} configuration first and, if templates are detected, will be used instead. Otherwise, Milestoner falls back to it's own templates. Once {runcom_link} has calculated all possible template locations, {hanami_views_link} handles the final loading and rendering of the templates. ==== Formats Of all build formats supported, the web format is the most powerful and feature rich. The following showcases _some_ of the information rendered in this format based on commit activity. *With Basic Git Commit* image:https://alchemists.io/images/projects/milestoner/screenshots/web_format-overview.png[Overview,width=1119,height=902,role=focal_point] *With Collaborators and Signers* image:https://alchemists.io/images/projects/milestoner/screenshots/web_format-collaborators_and_signers.png[Collaborators and Signers,width=903,height=903,role=focal_point] *With Invalid Tag* image:https://alchemists.io/images/projects/milestoner/screenshots/web_format-tag_invalid.png[Invalid Tag,width=881,height=403,role=focal_point] *With Valid Tag* image:https://alchemists.io/images/projects/milestoner/screenshots/web_format-tag_valid.png[Valid Tag,width=688,height=771,role=focal_point] *With Notes* image:https://alchemists.io/images/projects/milestoner/screenshots/web_format-notes.png[Notes,width=903,height=757,role=focal_point] *With ASCII Doc/Markdown* image:https://alchemists.io/images/projects/milestoner/screenshots/web_format-asdcii_doc[ASCII Doc,width=908,height=1938,role=focal_point] 💡 See {git_notes_link} to learn more. You'll find all formats render similar information with the feed format being the closest but most formats are not as feature rich as the web format. Each milestone is meant to provide you with the right amount of statistical information you can make informed decisions. == Security To securely sign your {git_link} tags, install and configure https://www.gnupg.org[GPG]: [source,bash] ---- brew install gpg gpg --gen-key ---- When creating your GPG key, choose these settings: * Key kind: RSA and RSA (default) * Key size: 4096 * Key validity: 0 * Real Name: `++` * Email: `++` * Passphrase: `++` To obtain your key, run the following and take the part after the forward slash: .... gpg --list-keys | grep pub .... Add your key to your global (or local) {git_link} configuration and ensure GPG signing for your tag is enabled. Example: .... [tag] gpgSign = true [user] signingkey = .... Now, when publishing a new milestone (i.e. `milestoner --publish `), the signing of your {git_link} tag will happen automatically. You will be prompted for the GPG Passphrase each time unless you are running the link:https://gnupg.org/documentation/manuals/gnupg/Invoking-GPG_002dAGENT.html#Invoking-GPG_002dAGENT[GPG Agent] in the background (highly recommend). == Development To contribute, run: [source,bash] ---- git clone https://github.com/bkuhlmann/milestoner cd milestoner bin/setup ---- You can also use the IRB console for direct access to all objects: [source,bash] ---- bin/console ---- == Tests To test, run: [source,bash] ---- bundle exec spec ---- == link:https://alchemists.io/policies/license[License] == link:https://alchemists.io/policies/security[Security] == link:https://alchemists.io/policies/code_of_conduct[Code of Conduct] == link:https://alchemists.io/policies/contributions[Contributions] == link:https://alchemists.io/policies/developer_certificate_of_origin[Developer Certificate of Origin] == link:https://alchemists.io/projects/milestoner/versions[Versions] == link:https://alchemists.io/community[Community] == Credits * Built with link:https://alchemists.io/projects/gemsmith[Gemsmith]. * Engineered by link:https://alchemists.io/team/brooke_kuhlmann[Brooke Kuhlmann].