diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..0ae8525 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,10 @@ +Dockerfile +CHANGELOG.md +CODE_OF_CONDUCT.md +Rakefile +test/ +vendor/ +vendor +test +*.gem +.git diff --git a/.gitignore b/.gitignore index a174504..78356df 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ /tmp/ /vendor/ Gemfile.lock +*.gem diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..731648a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,9 @@ +FROM ruby:2.6-alpine + +WORKDIR /outliner +COPY . /outliner/ + +RUN gem install bundler && \ + bundle install + +ENTRYPOINT ["/outliner/entrypoint.sh"] diff --git a/README.md b/README.md index f645946..485bf06 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Outliner [![Gem Version](https://badge.fury.io/rb/outliner.svg)](https://badge.fury.io/rb/outliner) -A simple HTTParty based wrapper for the [Outline API](https://www.getoutline.com/developers). It also offers a one-line import option to let you migrate an existing set of Markdown files to Outline. +A simple HTTParty based wrapper for the [Outline API](https://www.getoutline.com/developers). It also offers a one-line import option to let you migrate an existing set of Markdown files to Outline. For quickly running export/import commands, you can use the Docker Image as well. ## Installation @@ -32,7 +32,7 @@ pp client.collections_list(offset: 0, limit: 10) ### Import -`outliner` can be used to import an existing collection of documents into Outline. To do this run: +`outliner` can be used to import an existing collection of documents into Outline. To do this, run: ```bash export OUTLINE_BASE_URI="https://kb.example.com" @@ -40,12 +40,50 @@ export OUTLINE_TOKEN="PUT YOUR TOKEN HERE" export SOURCE_DIRECTORY="/home/user/wiki" export DESTINATION_COLLECTION_NAME="Archive" bundle install outliner -bundle exec bin/import "$SOURCE_DIRECTORY" "$DESTINATION_COLLECTION_NAME" +outliner-import "$SOURCE_DIRECTORY" "$DESTINATION_COLLECTION_NAME" +``` + +### Export + +`outliner` can be used to run a one-time export of all documents in Outline to a local directory. To do this, run: + +```bash +export OUTLINE_BASE_URI="https://kb.example.com" +export OUTLINE_TOKEN="PUT YOUR TOKEN HERE" +# Ensure that this exists and is writable +export DESTINATION_DIRECTORY="/data" +bundle install outliner +outliner-export "$DESTINATION_DIRECTORY" +``` + +## Docker + +You can use the pre-built docker image to run the above commands as well. See the following commands for examples: + +### Export + +```bash +docker run --env OUTLINE_BASE_URI="https://kb.example.com" \ + --env OUTLINE_TOKEN="PUT YOUR TOKEN HERE" \ + --volume /tmp:/data \ + captn3m0/outliner \ + export \ + /data +``` + +### Import + +```bash +docker run --env OUTLINE_BASE_URI="https://kb.example.com" \ + --env OUTLINE_TOKEN="PUT YOUR TOKEN HERE" \ + --volume /path/to/wiki:/data \ + captn3m0/outliner \ + import "/data" "Archive" ``` #### Limitations -- Images are currently not imported +- Images are currently not imported. Host them externally for this to work. - Only `.md` files are currently supported ## Development diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100755 index 0000000..446edb2 --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,21 @@ +#!/bin/sh + +if [ $# -eq 0 ]; then + echo "Please run with outliner [export|import] arguments" + exit +fi + +case $1 in + export) + shift + bundle exec outliner-export $@ + ;; + import) + shift + bundle exec outliner-import $@ + break + ;; + *) + echo "Invalid command, please check README" + ;; +esac diff --git a/exe/outliner-export b/exe/outliner-export new file mode 100755 index 0000000..cdcac3a --- /dev/null +++ b/exe/outliner-export @@ -0,0 +1,33 @@ +#!/usr/bin/env ruby + +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 +end + +# Run validations +validate + +# Setup variables +local_directory = ARGV[0] +CLIENT = Outliner::Client.new ENV['OUTLINE_BASE_URI'] + +# Download the complete zip +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) } + +`unzip -o "#{file.path}" -d "#{local_directory}"` + +# Delete tempfile +file.unlink diff --git a/bin/import b/exe/outliner-import similarity index 78% rename from bin/import rename to exe/outliner-import index 601a2f7..6f1c7c1 100755 --- a/bin/import +++ b/exe/outliner-import @@ -4,20 +4,11 @@ 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') - puts "[E] Please call as import local_directory remote_collection_name" + 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" - end -end - -def find_or_create_collection(name) - collections = CLIENT.collections_list(limit: 100)['data'] - collections.filter!{|c|c['name'] == name} - if collections.size >= 1 - collections[0]['id'] - else - CLIENT.collections_create(name: name, description: 'Imported Collection')['data']['id'] + exit 1 end end @@ -63,7 +54,7 @@ remote_collection_name = ARGV[1] # Create a root collection CLIENT = Outliner::Client.new ENV['OUTLINE_BASE_URI'] -root_collection_id = find_or_create_collection(remote_collection_name) +root_collection_id = find_or_create_collection(CLIENT, remote_collection_name) begin create_documents_recursively(local_directory, root_collection_id) diff --git a/lib/outliner.rb b/lib/outliner.rb index 12ba229..7a5e3a3 100644 --- a/lib/outliner.rb +++ b/lib/outliner.rb @@ -1,4 +1,5 @@ require "outliner/version" +require "outliner/client" module Outliner class Error < StandardError; end diff --git a/lib/outliner/helper.rb b/lib/outliner/helper.rb new file mode 100644 index 0000000..f93c392 --- /dev/null +++ b/lib/outliner/helper.rb @@ -0,0 +1,13 @@ +module Outliner + class Helper + def find_or_create_collection(client, name) + collections = client.collections_list(limit: 100)['data'] + collections.filter!{|c|c['name'] == name} + if collections.size >= 1 + collections[0]['id'] + else + client.collections_create(name: name, description: 'Imported Collection')['data']['id'] + end + end + end +end diff --git a/lib/outliner/version.rb b/lib/outliner/version.rb index 0ed039a..db2a4ad 100644 --- a/lib/outliner/version.rb +++ b/lib/outliner/version.rb @@ -1,3 +1,3 @@ module Outliner - VERSION = "0.1.1" + VERSION = "0.2.0" end diff --git a/outliner.gemspec b/outliner.gemspec index cc8be6e..9aeda22 100644 --- a/outliner.gemspec +++ b/outliner.gemspec @@ -2,13 +2,13 @@ lib = File.expand_path("lib", __dir__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require "outliner/version" -require "outliner/client" Gem::Specification.new do |spec| spec.name = "outliner" spec.version = Outliner::VERSION spec.authors = ["Nemo"] spec.email = ["rubygem.outliner@captnemo.in"] + spec.licenses = ["MIT"] spec.summary = "A simple HTTParty based client for outline knowledge base." spec.homepage = "https://github.com/captn3m0/outliner" @@ -17,11 +17,8 @@ Gem::Specification.new do |spec| spec.metadata["source_code_uri"] = "https://github.com/captn3m0/outliner" spec.metadata["changelog_uri"] = "https://github.com/captn3m0/outliner/blob/master/CHANGELOG.md" - # Specify which files should be added to the gem when it is released. - # The `git ls-files -z` loads the files in the RubyGem that have been added into git. - spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do - `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } - end + spec.files = Dir['**/*'].reject { |f| f.match(%r{^(vendor|test|spec|features)/}) } + spec.bindir = "exe" spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ["lib"]