Skip to content

Instantly share code, notes, and snippets.

@wsalesky
Created October 19, 2018 15:12
Show Gist options
  • Save wsalesky/b05c118d44ff04d49a500c5db032c330 to your computer and use it in GitHub Desktop.
Save wsalesky/b05c118d44ff04d49a500c5db032c330 to your computer and use it in GitHub Desktop.
eXist-db git commit as a module. Can be used with XForms to pass XML content to GitHub.
xquery version "3.1";
(:~
: POC: eXist-db module to commit content to github reop via github API
: Code creates a new branch off of the master branch, commits updated content to new branch and then submits a pull request when complete.
: Intended use is for online data submission, using GitHub for review/approval process.
:
: Prerequisites:
: In order to run the module, you will need a github Authorization token.
: When you create authorization token your OAuth Scope will need to include the github repository you would like to commit to.
: @see https://github.com/blog/1509-personal-api-tokens
:
: Github API Steps for creating/updating repository contents:
: 1. Get a reference to HEAD of branch, in this case we use master
: Save sha and url from github response
: 2. create a new branch
: Save sha and url from github response on the new branch
: 3. Get the latest commit for the new branch
: Save sha, tree sha and tree url
: 4. Post your content to github
: Save sha
: 5. Create a tree containing your new content
: Save sha
: 6. Create a new commit
: Save commit sha
: 7. Update refs with new commit information
: 8. Create a pull request
: If step 7 returns @status='200' create a pull request.
:
: Links to useful github api info
: https://developer.github.com/v3/
: https://developer.github.com/v3/git/
: https://gist.github.com/jasonrudolph/10727108
: http://www.mdswanson.com/blog/2011/07/23/digging-around-the-github-api-take-2.html
: http://www.levibotelho.com/development/commit-a-file-with-the-github-api/ (*This was the most helpful)
: http://patrick-mckinley.com/tech/github-api-commit.html
:
: @author Winona Salesky
: @version 1.0
:)
module namespace gitcommit="http://syriaca.org/srophe/gitcommit";
import module namespace http="http://expath.org/ns/http-client";
declare namespace output="http://www.w3.org/2010/xslt-xquery-serialization";
declare namespace json = "http://www.json.org";
(: Set up global varaibles :)
declare variable $gitcommit:repo {'YOUR GITHUB REPOSITORY'};
declare variable $gitcommit:authorization-token {'YOUR GITHUB AUTHORIZATION TOKEN'};
declare function gitcommit:step1($data as item()*,$path as xs:string?, $commit-message as xs:string?){
(: 1. Get a reference to HEAD of branch, master:)
let $branch :=
http:send-request(<http:request http-version="1.1" href="{xs:anyURI(concat($gitcommit:repo,'/git/refs/heads/master'))}" method="get">
<http:header name="Authorization" value="{concat('token ',$gitcommit:authorization-token)}"/>
<http:header name="Connection" value="close"/>
</http:request>)
let $branch-data := util:base64-decode($branch[2])
(: Get latest commit SHA from master branch :)
let $branch-sha := parse-json($branch-data)?object?sha
return
if(starts-with(string($branch[1]/@status),'2')) then
('Success, step1: ', gitcommit:step2($data,$path, $commit-message, $branch-sha))
else string($branch[1]/@message)
};
declare function gitcommit:step2($data as item()*,$path as xs:string?, $commit-message as xs:string?, $branch-sha as xs:string?){
(: 2. create a new branch :)
let $branch-name := concat(replace($path,'/|\.',''),'-',replace(util:random(),'\.',''))
let $new-branch-name :=
serialize(
<object>
<ref>refs/heads/{$branch-name}</ref>
<sha>{$branch-sha}</sha>
</object>,
<output:serialization-parameters>
<output:method>json</output:method>
</output:serialization-parameters>)
let $new-branch := http:send-request(
<http:request http-version="1.1" href="{xs:anyURI(concat($gitcommit:repo,'/git/refs'))}" method="post">
<http:header name="Authorization" value="{concat('token ',$gitcommit:authorization-token)}"/>
<http:header name="Connection" value="close"/>
<http:header name="Accept" value="application/json"/>
<http:body media-type="application/json" method="text">{$new-branch-name}</http:body>
</http:request>)
let $new-branch-data := util:base64-decode($new-branch[2])
(: Get latest commit SHA from master branch :)
let $new-branch-sha := parse-json($new-branch-data)?object?sha
(: Get latest commit SHA from master branch :)
let $new-branch-url := parse-json($new-branch-data)?object?url
return
if(starts-with(string($new-branch[1]/@status),'2')) then
('Succeses, step2: ',gitcommit:step3($data,$path,$commit-message, $branch-name, $branch-sha, $new-branch-sha, $new-branch-url))
else ('Fail step2. ',$new-branch-data)
};
declare function gitcommit:step3($data as item()*,
$path as xs:string?, $commit-message as xs:string?,
$branch-name as xs:string?, $branch-sha as xs:string?, $new-branch-sha as xs:string?,
$new-branch-url as xs:string?){
(: 3. Get the latest commit for the new branch :)
let $get-latest-commit := http:send-request(
<http:request http-version="1.1" href="{xs:anyURI($new-branch-url)}" method="get">
<http:header name="Authorization" value="{concat('token ',$gitcommit:authorization-token)}"/>
<http:header name="Connection" value="close"/>
</http:request>)
let $get-latest-commit-data := util:base64-decode($get-latest-commit[2])
(:Note the commit SHA, the tree SHA, and the tree URL.:)
let $get-latest-commit-sha := parse-json($get-latest-commit-data)?sha
let $get-latest-commit-tree-sha := parse-json($get-latest-commit-data)?tree?sha
let $get-latest-commit-tree-url := parse-json($get-latest-commit-data)?tree?url
return
if(starts-with(string($get-latest-commit[1]/@status),'2')) then
('Succeses, step3: ',gitcommit:step4($data, $path, $commit-message, $branch-name, $get-latest-commit-sha, $get-latest-commit-tree-url))
else ('Fail step 3 ',string($get-latest-commit[1]/@message))
};
declare function gitcommit:step4($data as item()*,
$path as xs:string?, $commit-message as xs:string?, $branch-name as xs:string?,
$get-latest-commit-sha as xs:string?, $get-latest-commit-tree-url as xs:string?){
(: 4. Post your content to github :)
(: Create new blob with new content base64/utf-8 :)
let $xml-data := serialize($data,
<output:serialization-parameters>
<output:method>xml</output:method>
</output:serialization-parameters>)
let $new-blob-content :=
serialize(
<object>
<content>{$xml-data}</content>
<encoding>utf-8</encoding>
</object>,
<output:serialization-parameters>
<output:method>json</output:method>
</output:serialization-parameters>)
let $new-blob :=
http:send-request(<http:request http-version="1.1" href="{xs:anyURI(concat($gitcommit:repo,'/git/blobs'))}" method="post">
<http:header name="Authorization" value="{concat('token ',$gitcommit:authorization-token)}"/>
<http:header name="Connection" value="close"/>
<http:header name="Accept" value="application/json"/>
<http:body media-type="application/json" method="text">{$new-blob-content}</http:body>
</http:request>)
let $new-blob-data := util:base64-decode($new-blob[2])
let $blob-sha := parse-json($new-blob-data)?sha
return
if(starts-with(string($new-blob[1]/@status),'2')) then
let $get-tree :=
http:send-request(<http:request http-version="1.1" href="{xs:anyURI($get-latest-commit-tree-url)}" method="get">
<http:header name="Authorization" value="{concat('token ',$gitcommit:authorization-token)}"/>
<http:header name="Connection" value="close"/>
</http:request>)
let $get-tree-data := util:base64-decode($get-tree[2])
let $get-tree-sha := parse-json($get-tree-data)?sha
return
if(starts-with(string($get-tree[1]/@status),'2')) then
('Succeses, step 4:', gitcommit:step5($path, $commit-message, $branch-name, $get-latest-commit-sha, $get-latest-commit-tree-url, $get-tree-sha, $blob-sha))
else ('Fail step 4.b ',string($get-tree[1]/@message))
else ('Fail step 4 ',string($new-blob[1]/@message), $new-blob-content)
};
declare function gitcommit:step5($path as xs:string?,
$commit-message as xs:string?,
$branch-name as xs:string?,
$get-latest-commit-sha as xs:string?,
$get-latest-commit-tree-url as xs:string?,
$get-tree-sha as xs:string?,
$blob-sha as xs:string?){
(: 5. Create a tree containing your new content :)
let $new-tree-content :=
serialize(
<object>
<base_tree>{$get-tree-sha}</base_tree>
<tree json:array="true">
<path>{$path}</path>
<mode>100644</mode>
<type>blob</type>
<sha>{$blob-sha}</sha>
</tree>
</object>,
<output:serialization-parameters>
<output:method>json</output:method>
</output:serialization-parameters>)
let $new-tree := http:send-request(
<http:request http-version="1.1" href="{xs:anyURI(concat($gitcommit:repo,'/git/trees'))}" method="post">
<http:header name="Authorization" value="{concat('token ',$gitcommit:authorization-token)}"/>
<http:header name="Connection" value="close"/>
<http:header name="Accept" value="application/json"/>
<http:body media-type="application/json" method="text">{$new-tree-content}</http:body>
</http:request>)
let $new-tree-data := util:base64-decode($new-tree[2])
let $get-new-tree-sha := parse-json($new-tree-data)?sha
return
if(starts-with(string($new-tree[1]/@status),'2')) then
('Succeses, step5: ',gitcommit:step6($path, $commit-message, $branch-name, $get-latest-commit-sha, $get-new-tree-sha))
else ('Fail step 5 ',$new-tree[1], $new-tree-data)
};
declare function gitcommit:step6($path as xs:string?,
$commit-message as xs:string?,
$branch-name as xs:string?,
$get-latest-commit-sha as xs:string?,
$get-new-tree-sha as xs:string?){
(: 6. Create a new commit :)
(: Update refs in repository to your new commit SHA :)
let $commit-ref-data :=
serialize(
<object>
<message>{$commit-message}</message>
<parents json:array="true">{$get-latest-commit-sha}</parents>
<tree>{$get-new-tree-sha}</tree>
</object>,
<output:serialization-parameters>
<output:method>json</output:method>
</output:serialization-parameters>)
let $commit := http:send-request(
<http:request http-version="1.1" href="{xs:anyURI(concat($gitcommit:repo,'/git/commits'))}" method="post">
<http:header name="Authorization" value="{concat('token ',$gitcommit:authorization-token)}"/>
<http:header name="Connection" value="close"/>
<http:header name="Accept" value="application/json"/>
<http:body media-type="application/json" method="text">{$commit-ref-data}</http:body>
</http:request>)
let $commit-data := util:base64-decode($commit[2])
let $commit-sha := parse-json($commit-data)?sha
return
if(starts-with(string($commit[1]/@status),'2')) then
('Succeses, step6: ',gitcommit:step7($path, $branch-name, $commit-sha))
else ('Fail step 6 ',string($commit[1]/@message))
};
declare function gitcommit:step7($path as xs:string?, $branch-name as xs:string?, $commit-sha as xs:string?){
(: 7. Update refs :)
let $update-ref :=
serialize(
<object>
<sha>{$commit-sha}</sha>
<force json:literal="true">true</force>
</object>,
<output:serialization-parameters>
<output:method>json</output:method>
</output:serialization-parameters>)
let $commit-ref := http:send-request(
<http:request http-version="1.1" href="{xs:anyURI(concat($gitcommit:repo,'/git/refs/heads/',$branch-name))}" method="post">
<http:header name="Authorization" value="{concat('token ',$gitcommit:authorization-token)}"/>
<http:header name="Connection" value="close"/>
<http:header name="Accept" value="application/json"/>
<http:body media-type="application/json" method="text">{$update-ref}</http:body>
</http:request>)[1]
return
if(starts-with(string($commit-ref/@status),'2')) then
let $pull-request-data :=
serialize(
<object>
<title>Updates from online corrections. {$path}</title>
<body>Review submitted changes and merge into master if acceptable.</body>
<head>{$branch-name}</head>
<base>master</base>
</object>,
<output:serialization-parameters>
<output:method>json</output:method>
</output:serialization-parameters>)
let $pull-request := http:send-request(
<http:request http-version="1.1" href="{xs:anyURI(concat($gitcommit:repo,'/pulls'))}" method="post">
<http:header name="Authorization" value="{concat('token ',$gitcommit:authorization-token)}"/>
<http:header name="Connection" value="close"/>
<http:header name="Accept" value="application/json"/>
<http:body media-type="application/json" method="text">{$pull-request-data}</http:body>
</http:request>)
let $pull-request-data := util:base64-decode($pull-request[2])
return string($pull-request[1]/@message)
else ('Fail, step 7',$commit-ref/@message)
};
declare function gitcommit:run-commit($data as item()*, $path as xs:string?, $commit-message as xs:string?) {
if(not(empty($data))) then
gitcommit:step1($data, $path, $commit-message)
else 'No data submitted'
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment