From 88902f6540a67916eb9233ac5cfe174219017af4 Mon Sep 17 00:00:00 2001 From: Gabriel Arazas Date: Sun, 5 Mar 2023 09:48:11 +0800 Subject: [PATCH] Add GitHub raw content include processor --- lib/asciidoctor/foodogsquared-extensions.rb | 2 + .../README.adoc | 33 ++++++++++ .../extension.rb | 62 +++++++++++++++++++ 3 files changed, 97 insertions(+) create mode 100644 lib/asciidoctor/github-raw-content-include-processor/README.adoc create mode 100644 lib/asciidoctor/github-raw-content-include-processor/extension.rb diff --git a/lib/asciidoctor/foodogsquared-extensions.rb b/lib/asciidoctor/foodogsquared-extensions.rb index f3930d0..ed9c274 100644 --- a/lib/asciidoctor/foodogsquared-extensions.rb +++ b/lib/asciidoctor/foodogsquared-extensions.rb @@ -2,8 +2,10 @@ require 'asciidoctor' require 'asciidoctor/extensions' require_relative 'man-inline-macro/extension' require_relative 'swhid-inline-macro/extension' +require_relative 'github-raw-content-include-processor/extension' Asciidoctor::Extensions.register do inline_macro ManInlineMacro inline_macro SWHInlineMacro + include_processor GitHubRawIncludeProcessor end diff --git a/lib/asciidoctor/github-raw-content-include-processor/README.adoc b/lib/asciidoctor/github-raw-content-include-processor/README.adoc new file mode 100644 index 0000000..6b1dcbe --- /dev/null +++ b/lib/asciidoctor/github-raw-content-include-processor/README.adoc @@ -0,0 +1,33 @@ += GitHub raw content include processor +:toc: + + +This is a link:https://docs.asciidoctor.org/asciidoctor/latest/extensions/include-processor/[include processor] for easily including files from GitHub. +Take note this will only include files. +For directories, submodules, or symlinks: they will not be processed and a warning will be issued. + +This extension honors the link:https://docs.asciidoctor.org/asciidoctor/latest/safe-modes/[safe mode setting] and link:https://docs.asciidoctor.org/asciidoc/latest/directives/include-uri/[the prerequisites for permitting includes with URI]. + +The following is the basic form of using this include processor. + +[source, asciidoc] +---- +include::github:$OWNER/$REPO[] +---- + + +== Attributes + +- `path` for the path of the file to be included. +This is practically required as the root entry of the repository is a directory. + +- `rev` is the name of the commit/tag/branch to be checked out. + + +== Example usage + +- `include::github:asciidoctor/asciidoctor[path=README.adoc, rev=v2.0.0]` will include the Asciidoc file from the link:https://github.com/asciidoctor/asciidoctor/[Asciidoctor GitHub repo] from the point of `v2.0.0`. + +- `include::github:NixOS/nixpkgs[path=shell.nix]` will get the latest revision of `shell.nix` from link:https://github.com/NixOS/nixpkgs[nixpkgs repository]. + +- `include::github:foo-dogsquared/nixos-config[]` should not be processed considering it points to the root directory of the repository. diff --git a/lib/asciidoctor/github-raw-content-include-processor/extension.rb b/lib/asciidoctor/github-raw-content-include-processor/extension.rb new file mode 100644 index 0000000..2e9272a --- /dev/null +++ b/lib/asciidoctor/github-raw-content-include-processor/extension.rb @@ -0,0 +1,62 @@ +require 'base64' +require 'json' +require 'open-uri' +require 'uri' + +class GitHubRawIncludeProcessor < Asciidoctor::Extensions::IncludeProcessor + def handles? target + target.start_with? 'github:' + end + + def warn_or_raise doc, warning + if (doc.safe > Asciidoctor::SafeMode::SERVER) || !(doc.attr? 'allow-uri-read') + raise warning + else + warn warning + end + end + + def process doc, reader, target, attrs + github_src = target.delete_prefix('github:').split('/', 3) + github_owner = github_src.at 0 + github_repo = github_src.at 1 + repo = "#{github_owner}/#{github_repo}" + + path = attrs['path'] || '' + uri = URI.parse %(https://api.github.com/repos/#{github_owner}/#{github_repo}/contents/#{path}) + + if attrs['rev'] + query = { :ref => attrs['rev'] } + uri.query = URI.encode_www_form query + end + + begin + OpenURI.open_uri( + uri, + 'X-GitHub-Api-Version' => '2022-11-28' + ) do |f| + response = JSON.parse(f.read) + + # If the response is an array, it is likely to be a directory. In this + # usecase, we'll just list them. + content = if response.kind_of? Array + warning = %(given path '#{path}' from GitHub repo '#{repo}' is a directory) + warn_or_raise doc, warning + warning + elsif response.kind_of? Object + if response['content'] && response['encoding'] == 'base64' + Base64.decode64 response['content'] + end + end + + reader.push_include content, target, target, 1, attrs + end + rescue OpenURI::HTTPError => e + warning = %(no such file or directory '#{path}' in GitHub repo '#{repo}') + warn_or_raise doc, warning + reader.push_include warning, target, target, 1, attrs + end + + reader + end +end