mirror of https://github.com/captn3m0/outliner.git
commit
425d55e30f
|
@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## 0.2.1 - 2019-08-14
|
||||||
|
|
||||||
|
- Adds a `push` command (See #2)
|
||||||
|
|
||||||
## 0.2.0 - 2019-08-12
|
## 0.2.0 - 2019-08-12
|
||||||
|
|
||||||
- Adds export command (See [#1013](https://github.com/outline/outline/pull/1013) for corresponding Outline PR)
|
- Adds export command (See [#1013](https://github.com/outline/outline/pull/1013) for corresponding Outline PR)
|
||||||
|
|
|
@ -4,6 +4,9 @@ WORKDIR /outliner
|
||||||
COPY . /outliner/
|
COPY . /outliner/
|
||||||
|
|
||||||
RUN gem install bundler && \
|
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"]
|
ENTRYPOINT ["/outliner/entrypoint.sh"]
|
||||||
|
|
17
README.md
17
README.md
|
@ -81,10 +81,27 @@ docker run --env OUTLINE_BASE_URI="https://kb.example.com" \
|
||||||
import "/data" "Archive"
|
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
|
#### Limitations
|
||||||
|
|
||||||
- Images are currently not imported. Host them externally for this to work.
|
- Images are currently not imported. Host them externally for this to work.
|
||||||
- Only `.md` files are currently supported
|
- 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
|
## Development
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,63 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
if [ $# -eq 0 ]; then
|
if [ $# -eq 0 ]; then
|
||||||
echo "Please run with outliner [export|import] arguments"
|
echo "Please run with outliner [export|import|sync] arguments"
|
||||||
exit
|
exit
|
||||||
fi
|
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
|
case $1 in
|
||||||
export)
|
export)
|
||||||
shift
|
shift
|
||||||
bundle exec outliner-export $@
|
bundle exec outliner-export "$@"
|
||||||
;;
|
;;
|
||||||
import)
|
import)
|
||||||
shift
|
shift
|
||||||
bundle exec outliner-import $@
|
bundle exec outliner-import "$@"
|
||||||
break
|
;;
|
||||||
|
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"
|
echo "Invalid command, please check README"
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "bundler/setup"
|
require 'bundler/setup'
|
||||||
require "outliner"
|
require 'outliner'
|
||||||
require 'tempfile'
|
require 'tempfile'
|
||||||
|
|
||||||
def validate
|
def validate
|
||||||
unless (ARGV.size == 1) and Dir.exists?(ARGV[0]) and ENV.key?('OUTLINE_BASE_URI') and ENV.key?('OUTLINE_TOKEN')
|
raise 'Missing arguments' if ARGV.size != 1
|
||||||
puts "[E] Please call as `outliner-export directory`"
|
raise 'Invalid directory' unless Dir.exist?(ARGV[0])
|
||||||
puts "[E] Please export OUTLINE_BASE_URI and OUTLINE_TOKEN environment variables"
|
raise 'OUTLINE_BASE_URI not set' unless ENV.key?('OUTLINE_BASE_URI')
|
||||||
puts "[E] OUTLINE_BASE_URI should not include /api"
|
raise 'OUTLINE_TOKEN not set' unless ENV.key?('OUTLINE_TOKEN')
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Run validations
|
# Run validations
|
||||||
|
@ -25,7 +24,7 @@ response = CLIENT.collections_exportAll(download: true)
|
||||||
|
|
||||||
# Extract it to a tempfle
|
# Extract it to a tempfle
|
||||||
file = Tempfile.new('download.zip')
|
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}"`
|
`unzip -o "#{file.path}" -d "#{local_directory}"`
|
||||||
|
|
||||||
|
|
|
@ -1,25 +1,28 @@
|
||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "bundler/setup"
|
require 'bundler/setup'
|
||||||
require "outliner"
|
require 'outliner'
|
||||||
|
|
||||||
def validate
|
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')
|
raise 'Missing arguments' if ARGV.size != 2
|
||||||
puts "[E] Please call as `outliner-import local_directory remote_collection_name`"
|
raise 'Invalid directory' unless Dir.exist?(ARGV[0])
|
||||||
puts "[E] Please export OUTLINE_BASE_URI and OUTLINE_TOKEN environment variables"
|
raise 'Invalid collection' unless ARGV[1].match(/\w+/)
|
||||||
puts "[E] OUTLINE_BASE_URI should not include /api"
|
raise 'OUTLINE_BASE_URI not set' unless ENV.key?('OUTLINE_BASE_URI')
|
||||||
exit 1
|
raise 'OUTLINE_TOKEN not set' unless ENV.key?('OUTLINE_TOKEN')
|
||||||
end
|
|
||||||
end
|
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
|
cwd = Dir.pwd
|
||||||
Dir.chdir directory
|
Dir.chdir directory
|
||||||
# Create all documents for this directory
|
# Create all documents for this directory
|
||||||
Dir["*.md"].each do |file|
|
Dir['*.md'].each do |file|
|
||||||
params = {
|
params = {
|
||||||
title: file[0...-3],
|
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,
|
collectionId: collection_id,
|
||||||
publish: true
|
publish: true
|
||||||
}
|
}
|
||||||
|
@ -30,11 +33,11 @@ def create_documents_recursively(directory, collection_id, parent_document_id=ni
|
||||||
end
|
end
|
||||||
|
|
||||||
# Create child documents for each sub-directory
|
# 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}"
|
puts "[-] #{dir}"
|
||||||
params = {
|
params = {
|
||||||
title: dir,
|
title: dir,
|
||||||
text: dir +"\nImported at #{Time.now}",
|
text: dir + "\nImported at #{Time.now}",
|
||||||
collectionId: collection_id,
|
collectionId: collection_id,
|
||||||
publish: true,
|
publish: true,
|
||||||
parentDocumentId: parent_document_id
|
parentDocumentId: parent_document_id
|
||||||
|
@ -58,12 +61,11 @@ root_collection_id = CLIENT.find_or_create_collection(remote_collection_name)
|
||||||
|
|
||||||
begin
|
begin
|
||||||
create_documents_recursively(local_directory, root_collection_id)
|
create_documents_recursively(local_directory, root_collection_id)
|
||||||
puts "[S] Import successful"
|
puts '[S] Import successful'
|
||||||
rescue Exception => e
|
rescue StandardError? => e
|
||||||
# If we fail, print an error, and delete the collection
|
# If we fail, print an error, and delete the collection
|
||||||
puts "[E] Import failed with error: #{e.message}"
|
puts "[E] Import failed with error: #{e.message}"
|
||||||
CLIENT.collections_delete(id: root_collection_id)
|
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
|
exit 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue