From 156bc864579db7a28736f7af7b65aab7532a1816 Mon Sep 17 00:00:00 2001 From: Nemo Date: Mon, 12 Aug 2019 19:27:32 +0530 Subject: [PATCH 1/5] Adds git-sync support --- Dockerfile | 5 ++++- entrypoint.sh | 41 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 731648a..3aaf633 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,6 +4,9 @@ WORKDIR /outliner COPY . /outliner/ RUN gem install bundler && \ - bundle install + bundle install && \ + apk add --no-cache git && \ + echo -e "StrictHostKeyChecking no" >> /etc/ssh/ssh_config && \ + mkdir /root/.ssh ENTRYPOINT ["/outliner/entrypoint.sh"] diff --git a/entrypoint.sh b/entrypoint.sh index 446edb2..1fd8e4e 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -5,15 +5,50 @@ if [ $# -eq 0 ]; then exit fi +setup_git() { + chmod 0400 /root/.ssh/id_rsa + + if [ -f "$HOME.ssh/id_rsa" ]; then + chmod 600 "$HOME.ssh/id_rsa" + + if [ ! -d "$HOME.ssh/id_rsa.pub" ]; then + ssh-keygen -y -f "$HOME.ssh/id_rsa" > "$HOME.ssh/id_rsa.pub" + fi + + echo "[+] Using SSH key for git pushes" + else + echo "[E] Git credentials not available, quitting" + exit 0 + fi +} + +update_git_repo() { + BRANCH=${GIT_BRANCH:-master} + # Clone the branch in a temporary directory + tmp_dir=$(mktemp -d) + git clone "$GIT_REMOTE_URL" "$tmp_dir" + cp -r "$1/*" "$tmp_dir/" + cd "$tmp_dir" || exit 1 + git add . + git commit -m "Updates: $(date)" + git push origin "$BRANCH" +} + case $1 in export) shift - bundle exec outliner-export $@ + bundle exec outliner-export "$@" + + if [ -z "$GIT_REMOTE_URL" ]; then + echo "GIT_REMOTE_URL not set" + else + setup_git + update_git_repo "$2" + fi ;; import) shift - bundle exec outliner-import $@ - break + bundle exec outliner-import "$@" ;; *) echo "Invalid command, please check README" From d31051f6f0c23784592cb30f105be52003e68d18 Mon Sep 17 00:00:00 2001 From: Nemo Date: Mon, 12 Aug 2019 19:27:49 +0530 Subject: [PATCH 2/5] [style] Fixes rubocop warnings --- exe/outliner-export | 17 ++++++++--------- exe/outliner-import | 36 +++++++++++++++++++----------------- 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/exe/outliner-export b/exe/outliner-export index cdcac3a..f12f103 100755 --- a/exe/outliner-export +++ b/exe/outliner-export @@ -1,16 +1,15 @@ #!/usr/bin/env ruby +# frozen_string_literal: true -require "bundler/setup" -require "outliner" +require 'bundler/setup' +require 'outliner' require 'tempfile' def validate - unless (ARGV.size == 1) and Dir.exists?(ARGV[0]) and ENV.key?('OUTLINE_BASE_URI') and ENV.key?('OUTLINE_TOKEN') - puts "[E] Please call as `outliner-export directory`" - puts "[E] Please export OUTLINE_BASE_URI and OUTLINE_TOKEN environment variables" - puts "[E] OUTLINE_BASE_URI should not include /api" - exit 1 - end + raise 'Missing arguments' if ARGV.size != 1 + raise 'Invalid directory' unless Dir.exist?(ARGV[0]) + raise 'OUTLINE_BASE_URI not set' unless ENV.key?('OUTLINE_BASE_URI') + raise 'OUTLINE_TOKEN not set' unless ENV.key?('OUTLINE_TOKEN') end # Run validations @@ -25,7 +24,7 @@ response = CLIENT.collections_exportAll(download: true) # Extract it to a tempfle file = Tempfile.new('download.zip') -File.open(file.path, 'w') { |file| file.write(response.body) } +File.open(file.path, 'w') { |f| f.write(response.body) } `unzip -o "#{file.path}" -d "#{local_directory}"` diff --git a/exe/outliner-import b/exe/outliner-import index 6a1222f..b2941a7 100755 --- a/exe/outliner-import +++ b/exe/outliner-import @@ -1,25 +1,28 @@ #!/usr/bin/env ruby +# frozen_string_literal: true -require "bundler/setup" -require "outliner" +require 'bundler/setup' +require 'outliner' def validate - unless (ARGV.size == 2) and Dir.exists?(ARGV[0]) and ARGV[1].match(/\w+/) and ENV.key?('OUTLINE_BASE_URI') and ENV.key?('OUTLINE_TOKEN') - puts "[E] Please call as `outliner-import local_directory remote_collection_name`" - puts "[E] Please export OUTLINE_BASE_URI and OUTLINE_TOKEN environment variables" - puts "[E] OUTLINE_BASE_URI should not include /api" - exit 1 - end + raise 'Missing arguments' if ARGV.size != 2 + raise 'Invalid directory' unless Dir.exist?(ARGV[0]) + raise 'Invalid collection' unless ARGV[1].match(/\w+/) + raise 'OUTLINE_BASE_URI not set' unless ENV.key?('OUTLINE_BASE_URI') + raise 'OUTLINE_TOKEN not set' unless ENV.key?('OUTLINE_TOKEN') end -def create_documents_recursively(directory, collection_id, parent_document_id=nil) +def create_documents_recursively(directory, collection_id, parent_document_id = nil) cwd = Dir.pwd Dir.chdir directory # Create all documents for this directory - Dir["*.md"].each do |file| + Dir['*.md'].each do |file| params = { title: file[0...-3], - text: file[0...-3] + "\n" + File.read(file) + "\n\n---\nImported at #{Time.now}", + text: file[0...-3] + + "\n" + + File.read(file) + + "\n\n---\nImported at #{Time.now}", collectionId: collection_id, publish: true } @@ -30,11 +33,11 @@ def create_documents_recursively(directory, collection_id, parent_document_id=ni end # Create child documents for each sub-directory - Dir.glob('*').select {|f| File.directory? f}.each do |dir| + Dir.glob('*').select { |f| File.directory? f }.each do |dir| puts "[-] #{dir}" params = { title: dir, - text: dir +"\nImported at #{Time.now}", + text: dir + "\nImported at #{Time.now}", collectionId: collection_id, publish: true, parentDocumentId: parent_document_id @@ -58,12 +61,11 @@ root_collection_id = CLIENT.find_or_create_collection(remote_collection_name) begin create_documents_recursively(local_directory, root_collection_id) - puts "[S] Import successful" -rescue Exception => e + puts '[S] Import successful' +rescue StandardError? => e # If we fail, print an error, and delete the collection puts "[E] Import failed with error: #{e.message}" CLIENT.collections_delete(id: root_collection_id) - puts "[E] Deleted collection, please report the issue or retry" + puts '[E] Deleted collection, please report the issue or retry' exit 1 end - From 1159aeb4fd3037c411f6a31290031ac85823414e Mon Sep 17 00:00:00 2001 From: Nemo Date: Tue, 13 Aug 2019 10:29:25 +0530 Subject: [PATCH 3/5] Adds openssh-client --- Dockerfile | 2 +- entrypoint.sh | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index 3aaf633..36d4483 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,7 @@ COPY . /outliner/ RUN gem install bundler && \ bundle install && \ - apk add --no-cache git && \ + apk add --no-cache git openssh-client && \ echo -e "StrictHostKeyChecking no" >> /etc/ssh/ssh_config && \ mkdir /root/.ssh diff --git a/entrypoint.sh b/entrypoint.sh index 1fd8e4e..20324ea 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -6,13 +6,13 @@ if [ $# -eq 0 ]; then fi setup_git() { - chmod 0400 /root/.ssh/id_rsa + chmod 0400 "$HOME/.ssh/id_rsa" - if [ -f "$HOME.ssh/id_rsa" ]; then + if [ -f "$HOME/.ssh/id_rsa" ]; then chmod 600 "$HOME.ssh/id_rsa" - if [ ! -d "$HOME.ssh/id_rsa.pub" ]; then - ssh-keygen -y -f "$HOME.ssh/id_rsa" > "$HOME.ssh/id_rsa.pub" + if [ ! -d "$HOME/.ssh/id_rsa.pub" ]; then + ssh-keygen -y -f "$HOME/.ssh/id_rsa" > "$HOME/.ssh/id_rsa.pub" fi echo "[+] Using SSH key for git pushes" From 09287e761251b92175a4b1809e1c24a84671cf61 Mon Sep 17 00:00:00 2001 From: Nemo Date: Tue, 13 Aug 2019 18:01:11 +0530 Subject: [PATCH 4/5] [git] Break git sync into separate command --- entrypoint.sh | 51 +++++++++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/entrypoint.sh b/entrypoint.sh index 20324ea..66439b1 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,4 +1,5 @@ #!/bin/sh +set -eu if [ $# -eq 0 ]; then echo "Please run with outliner [export|import] arguments" @@ -6,50 +7,56 @@ if [ $# -eq 0 ]; then fi setup_git() { - chmod 0400 "$HOME/.ssh/id_rsa" - if [ -f "$HOME/.ssh/id_rsa" ]; then - chmod 600 "$HOME.ssh/id_rsa" + chmod 0400 "$HOME/.ssh/id_rsa" if [ ! -d "$HOME/.ssh/id_rsa.pub" ]; then ssh-keygen -y -f "$HOME/.ssh/id_rsa" > "$HOME/.ssh/id_rsa.pub" fi - echo "[+] Using SSH key for git pushes" else echo "[E] Git credentials not available, quitting" - exit 0 + exit 1 fi + + eval $(ssh-agent) + ssh-add "$HOME/.ssh/id_rsa" } -update_git_repo() { - BRANCH=${GIT_BRANCH:-master} - # Clone the branch in a temporary directory - tmp_dir=$(mktemp -d) - git clone "$GIT_REMOTE_URL" "$tmp_dir" - cp -r "$1/*" "$tmp_dir/" - cd "$tmp_dir" || exit 1 - git add . - git commit -m "Updates: $(date)" - git push origin "$BRANCH" +update_git_config() { + EMAIL=${GIT_EMAIL:-outliner@example.invalid} + git config --global user.email "$EMAIL" + git config --global user.name "Outliner Backup" + git remote add origin "$GIT_REMOTE_URL" } case $1 in export) shift bundle exec outliner-export "$@" - - if [ -z "$GIT_REMOTE_URL" ]; then - echo "GIT_REMOTE_URL not set" - else - setup_git - update_git_repo "$2" - fi ;; import) shift bundle exec outliner-import "$@" ;; + sync) + tmp_dir=$(mktemp -d) + if [ -z "$GIT_REMOTE_URL" ]; then + echo "[E] GIT_REMOTE_URL not set" + exit 1 + else + setup_git + bundle exec outliner-export "$tmp_dir" + cd "$tmp_dir" + git init + update_git_config + git add . + git commit --message "Backup: $(date)" + BRANCH=${GIT_BRANCH:-master} + git checkout -b "$BRANCH" + git push origin --force "$BRANCH" + fi + ;; *) echo "Invalid command, please check README" ;; From d68dd6e79e62d51945203d2eaa20220a2c498b85 Mon Sep 17 00:00:00 2001 From: Nemo Date: Wed, 14 Aug 2019 15:09:11 +0530 Subject: [PATCH 5/5] [git] Finishes push command --- CHANGELOG.md | 4 ++++ README.md | 17 +++++++++++++++++ entrypoint.sh | 10 ++++++---- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 908bac2..5d5fd7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## 0.2.1 - 2019-08-14 + +- Adds a `push` command (See #2) + ## 0.2.0 - 2019-08-12 - Adds export command (See [#1013](https://github.com/outline/outline/pull/1013) for corresponding Outline PR) diff --git a/README.md b/README.md index fe117b6..f72a1bc 100644 --- a/README.md +++ b/README.md @@ -81,10 +81,27 @@ docker run --env OUTLINE_BASE_URI="https://kb.example.com" \ import "/data" "Archive" ``` +### Push + +Note: Push is currently only available as a Docker Command + +```bash +docker run --env OUTLINE_BASE_URI="https://kb.example.com" \ + --env OUTLINE_TOKEN="PUT YOUR TOKEN HERE" \ + --env OUTLINE_TOKEN="PUT YOUR TOKEN HERE" \ + --env GIT_BRANCH=outline \ + --env GIT_REMOTE_URL=git@example.com:org/outline.backup.git + --volume /etc/ssh/private.key:/root/.ssh/id_rsa + captn3m0/outliner \ + push +``` + #### Limitations - Images are currently not imported. Host them externally for this to work. - Only `.md` files are currently supported +- `push` doesn't sync file-history, but is meant to push a one-time backup to Git. +- `StrictHostKeyChecking` is currently disabled for `push`, please only run this in trusted networks. ## Development diff --git a/entrypoint.sh b/entrypoint.sh index 66439b1..4ac69cb 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,13 +1,14 @@ #!/bin/sh set -eu - if [ $# -eq 0 ]; then - echo "Please run with outliner [export|import] arguments" + echo "Please run with outliner [export|import|sync] arguments" exit fi setup_git() { if [ -f "$HOME/.ssh/id_rsa" ]; then + # This is required because Kubernetes secret mounts can't + # have file permissions set chmod 0400 "$HOME/.ssh/id_rsa" if [ ! -d "$HOME/.ssh/id_rsa.pub" ]; then @@ -51,10 +52,11 @@ case $1 in git init update_git_config git add . - git commit --message "Backup: $(date)" + git commit --message "Backup: $(date)" > /dev/null BRANCH=${GIT_BRANCH:-master} git checkout -b "$BRANCH" - git push origin --force "$BRANCH" + git status + git push origin --force "HEAD:$BRANCH" fi ;; *)