Skip to content

Commit

Permalink
git-push-for-backups
Browse files Browse the repository at this point in the history
git-syncing
  • Loading branch information
captn3m0 committed Aug 14, 2019
2 parents cf2ac7e + d68dd6e commit 425d55e
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 32 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Expand Up @@ -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)
Expand Down
5 changes: 4 additions & 1 deletion Dockerfile
Expand Up @@ -4,6 +4,9 @@ WORKDIR /outliner
COPY . /outliner/

RUN gem install bundler && \
bundle install
bundle install && \
apk add --no-cache git openssh-client && \
echo -e "StrictHostKeyChecking no" >> /etc/ssh/ssh_config && \
mkdir /root/.ssh

ENTRYPOINT ["/outliner/entrypoint.sh"]
17 changes: 17 additions & 0 deletions README.md
Expand Up @@ -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

Expand Down
54 changes: 49 additions & 5 deletions entrypoint.sh
@@ -1,19 +1,63 @@
#!/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
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 1
fi

eval $(ssh-agent)
ssh-add "$HOME/.ssh/id_rsa"
}

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 $@
bundle exec outliner-export "$@"
;;
import)
shift
bundle exec outliner-import $@
break
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)" > /dev/null
BRANCH=${GIT_BRANCH:-master}
git checkout -b "$BRANCH"
git status
git push origin --force "HEAD:$BRANCH"
fi
;;
*)
echo "Invalid command, please check README"
Expand Down
17 changes: 8 additions & 9 deletions 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
Expand All @@ -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}"`

Expand Down
36 changes: 19 additions & 17 deletions 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
}
Expand All @@ -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
Expand All @@ -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

0 comments on commit 425d55e

Please sign in to comment.