devlogs 0.2.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- data/Gemfile.lock +1 -1
- data/Makefile +16 -0
- data/README.md +5 -7
- data/lib/devlogs/cli.rb +38 -7
- data/lib/devlogs/editor.rb +3 -1
- data/lib/devlogs/executable.rb +23 -0
- data/lib/devlogs/gem.rb +7 -0
- data/lib/devlogs/log_template.rb +35 -0
- data/lib/devlogs/pager.rb +11 -0
- data/lib/devlogs/prompt_utils.rb +25 -0
- data/lib/devlogs/repository/config.rb +36 -0
- data/lib/devlogs/repository/config_store.rb +54 -0
- data/lib/devlogs/repository/initializer.rb +86 -0
- data/lib/devlogs/repository/sync_manager.rb +59 -0
- data/lib/devlogs/repository.rb +29 -163
- data/lib/devlogs/templates/__log_template.erb.md +5 -0
- data/lib/devlogs/version.rb +1 -1
- data/lib/devlogs.rb +1 -0
- metadata +13 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9df7671be5997a9e311b6a8c10863732b95fb3e6bdb89a7f5ba8476d3cec4653
|
4
|
+
data.tar.gz: af3731bb2c7134cea231b17a7c940d313d13f21c0721c720029e1084d706f0a4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ffe4be66ace5c88074c0ce3e8a20e0f82a53162e59776be3bfdbf1d72c038d4d73365ca65bf91047b4730a64efeb548f3e1b7f8a8704a0f9554bc427ed0daa39
|
7
|
+
data.tar.gz: 9f3326e754283f25d307c598aa8824c53aa5686c112ebb9c9301ee7dc424710c572cac02275f72cf204e5e00677c92c03bd7e3f4ee157e7d585c9a3bcd2fc6c9
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
data/Makefile
ADDED
data/README.md
CHANGED
@@ -23,12 +23,12 @@ Or install it yourself as:
|
|
23
23
|
|
24
24
|
## Usage
|
25
25
|
### Initialize
|
26
|
-
Inside your project initialize the `
|
26
|
+
Inside your project initialize the `.devlogs` repository:
|
27
27
|
```bash
|
28
28
|
$ devlogs init
|
29
29
|
```
|
30
30
|
|
31
|
-
Follow the prompts to setup the project configuration located in
|
31
|
+
Follow the prompts to setup the project configuration located in the _default_ `.devlogs/.devlogs.config`. (You can optionally set where you want to initialize the repository via --dirpath)
|
32
32
|
|
33
33
|
You can setup a mirror directory path in the configuration stage to sync changes to another directory on your machine, for example to Obsidian.md.
|
34
34
|
|
@@ -36,7 +36,7 @@ Example:
|
|
36
36
|
|
37
37
|
```
|
38
38
|
myproject
|
39
|
-
|
39
|
+
.devlogs
|
40
40
|
>> content
|
41
41
|
```
|
42
42
|
|
@@ -48,10 +48,10 @@ obsidianvault
|
|
48
48
|
```
|
49
49
|
|
50
50
|
### Creating entries
|
51
|
-
Once you are done for the day or session run the `
|
51
|
+
Once you are done for the day or session run the `new` command:
|
52
52
|
|
53
53
|
```bash
|
54
|
-
devlogs
|
54
|
+
devlogs new
|
55
55
|
```
|
56
56
|
|
57
57
|
Your editor will pop up and you can fill in cliff notes.
|
@@ -73,8 +73,6 @@ You can use the `last` command to retrieve the most recent entry
|
|
73
73
|
devlogs last
|
74
74
|
```
|
75
75
|
|
76
|
-
The `--open` command will cause the entry to be opened in a new default editor.
|
77
|
-
|
78
76
|
## Development
|
79
77
|
|
80
78
|
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/lib/devlogs/cli.rb
CHANGED
@@ -3,6 +3,9 @@
|
|
3
3
|
require_relative "version"
|
4
4
|
require_relative "repository"
|
5
5
|
require_relative "editor"
|
6
|
+
require_relative "pager"
|
7
|
+
require_relative "prompt_utils"
|
8
|
+
require_relative "repository/initializer"
|
6
9
|
require "thor"
|
7
10
|
|
8
11
|
module Devlogs
|
@@ -10,6 +13,8 @@ module Devlogs
|
|
10
13
|
# The CLI devlogs CLI
|
11
14
|
#
|
12
15
|
class CLI < Thor
|
16
|
+
include PromptUtils
|
17
|
+
|
13
18
|
package_name "devlogs"
|
14
19
|
|
15
20
|
# Returns exit with non zero status when an exception occurs
|
@@ -29,16 +34,19 @@ module Devlogs
|
|
29
34
|
# Initializes a +devlogs+ repository with a configuration
|
30
35
|
#
|
31
36
|
desc "init", "Initialize a developer logs for project"
|
32
|
-
method_options force: :boolean
|
37
|
+
method_options force: :boolean
|
38
|
+
method_options dirpath: :string
|
33
39
|
def init
|
34
40
|
puts "Creating devlogs repository"
|
35
41
|
|
36
|
-
Repository::
|
37
|
-
{
|
38
|
-
|
42
|
+
Repository::Initializer.run(
|
43
|
+
{
|
44
|
+
force: options.force?,
|
45
|
+
dirpath: options.dirpath,
|
46
|
+
},
|
39
47
|
)
|
40
48
|
|
41
|
-
puts "Created devlogs"
|
49
|
+
puts "Created devlogs repository"
|
42
50
|
end
|
43
51
|
|
44
52
|
#
|
@@ -60,19 +68,42 @@ module Devlogs
|
|
60
68
|
# Creates a devlogs entry in the repository and syncs changes
|
61
69
|
# to the mirrored directory if set
|
62
70
|
#
|
63
|
-
desc "
|
64
|
-
def
|
71
|
+
desc "new", "Create a new devlogs entry" # [4]
|
72
|
+
def new
|
65
73
|
puts "Creating new entry..."
|
66
74
|
repo.create
|
67
75
|
|
68
76
|
repo.sync
|
69
77
|
end
|
70
78
|
|
79
|
+
#
|
80
|
+
# Lists repository logs
|
81
|
+
#
|
82
|
+
desc "ls", "Lists the repository logs and allows you to select"
|
83
|
+
def ls
|
84
|
+
entries = repo.ls
|
85
|
+
|
86
|
+
if entries.size < 1
|
87
|
+
puts "No logs present in this repository"
|
88
|
+
exit 0
|
89
|
+
end
|
90
|
+
|
91
|
+
# Use the file names as visible keys for the prompt
|
92
|
+
entry_names = entries.map { |e| File.basename(e) }
|
93
|
+
|
94
|
+
# Build the TTY:Prompt
|
95
|
+
result = build_select_prompt(data: entry_names, text: "Select a log entry...")
|
96
|
+
|
97
|
+
# Open in paging program
|
98
|
+
Pager.open(entries[result])
|
99
|
+
end
|
100
|
+
|
71
101
|
private
|
72
102
|
|
73
103
|
# Helper method for repository loading
|
74
104
|
#
|
75
105
|
def repo
|
106
|
+
# FIXME: Need to add in path specification here
|
76
107
|
@repo ||= Repository.load
|
77
108
|
end
|
78
109
|
end
|
data/lib/devlogs/editor.rb
CHANGED
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Executable
|
4
|
+
def initialize
|
5
|
+
raise NotImplementedError, "Abstract class"
|
6
|
+
end
|
7
|
+
|
8
|
+
# Opens the file contained at the path
|
9
|
+
def open(path)
|
10
|
+
command = "#{@program} #{path}"
|
11
|
+
|
12
|
+
system command
|
13
|
+
end
|
14
|
+
|
15
|
+
class << self
|
16
|
+
# Opens the file at +path+ using system editor
|
17
|
+
def open(path)
|
18
|
+
session = new
|
19
|
+
|
20
|
+
session.open(path)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/devlogs/gem.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "erb"
|
4
|
+
|
5
|
+
#
|
6
|
+
# LogTemplate is a class that represents the rendered log template
|
7
|
+
#
|
8
|
+
class LogTemplate
|
9
|
+
attr_reader :time
|
10
|
+
|
11
|
+
TIME_FORMAT_TEXT_ENTRY = "%m-%d-%Y %k:%M"
|
12
|
+
|
13
|
+
def initialize(template_file_path)
|
14
|
+
@time = Time.new.strftime(TIME_FORMAT_TEXT_ENTRY)
|
15
|
+
@template_file_path = template_file_path
|
16
|
+
end
|
17
|
+
|
18
|
+
#
|
19
|
+
# Runs the ERB rendering using the provided template file template_file_path
|
20
|
+
#
|
21
|
+
# @returns [String]
|
22
|
+
#
|
23
|
+
def render
|
24
|
+
erb = ERB.new(File.read(@template_file_path))
|
25
|
+
erb.result(get_binding)
|
26
|
+
end
|
27
|
+
|
28
|
+
# rubocop:disable
|
29
|
+
#
|
30
|
+
# For ERB
|
31
|
+
#
|
32
|
+
def get_binding
|
33
|
+
binding
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "tty-prompt"
|
4
|
+
|
5
|
+
#
|
6
|
+
# Utility module for tty-prompt library
|
7
|
+
#
|
8
|
+
module PromptUtils
|
9
|
+
#
|
10
|
+
# Builds a basic select prompt using the provided data
|
11
|
+
#
|
12
|
+
# @param data [Array<String>]
|
13
|
+
#
|
14
|
+
# @returns String
|
15
|
+
#
|
16
|
+
def build_select_prompt(data:, text:)
|
17
|
+
ttyprompt = TTY::Prompt.new
|
18
|
+
|
19
|
+
ttyprompt.select(text) do |menu|
|
20
|
+
data.each_with_index do |d, index|
|
21
|
+
menu.choice d, index
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# The repository's configuration values located in the yml file
|
4
|
+
class Repository
|
5
|
+
class Config
|
6
|
+
# FIXME: Need to figure out file path
|
7
|
+
attr_reader :name, :description, :mirror, :file_path, :template_file_path
|
8
|
+
|
9
|
+
# Configuration associated with the Mirror
|
10
|
+
MirrorConfig = Struct.new(:use_mirror, :path, keyword_init: true)
|
11
|
+
|
12
|
+
def initialize(path, opts = {})
|
13
|
+
@file_path = path
|
14
|
+
@template_file_path = opts[:template_file_path]
|
15
|
+
@name = opts[:name]
|
16
|
+
@description = opts[:description]
|
17
|
+
@mirror = MirrorConfig.new(opts[:mirror])
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns whether or not the devlogs repository is configured to mirror
|
21
|
+
#
|
22
|
+
# @returns [Boolean]
|
23
|
+
def mirror?
|
24
|
+
@mirror.use_mirror
|
25
|
+
end
|
26
|
+
|
27
|
+
# Ensures no weird double trailing slash path values
|
28
|
+
def path(with_trailing: false)
|
29
|
+
if with_trailing
|
30
|
+
@file_path[-1] == "/" ? @path_value : @path_value + "/"
|
31
|
+
else
|
32
|
+
@file_path
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "yaml"
|
4
|
+
require "rsync"
|
5
|
+
|
6
|
+
require_relative "config"
|
7
|
+
|
8
|
+
# A per repository configuration storage directory
|
9
|
+
class Repository
|
10
|
+
class ConfigStore
|
11
|
+
attr_reader :dir, :values
|
12
|
+
|
13
|
+
CONFIG_FILE = ".devlogs.config.yml"
|
14
|
+
|
15
|
+
# TODO: should be part of configuration
|
16
|
+
TEMPLATE_FILE = ".log_template.erb.md"
|
17
|
+
DEFAULT_DIRECTORY_PATH = "."
|
18
|
+
DEFAULT_DIRECTORY_NAME = ".devlogs"
|
19
|
+
|
20
|
+
def initialize(dir: File.join(DEFAULT_DIRECTORY_PATH, DEFAULT_DIRECTORY_NAME))
|
21
|
+
@dir = dir
|
22
|
+
end
|
23
|
+
|
24
|
+
def values
|
25
|
+
@values ||= load_values_from_config_file
|
26
|
+
end
|
27
|
+
|
28
|
+
def file_path
|
29
|
+
File.join(@dir, CONFIG_FILE)
|
30
|
+
end
|
31
|
+
|
32
|
+
def template_file_path
|
33
|
+
File.join(@dir, TEMPLATE_FILE)
|
34
|
+
end
|
35
|
+
|
36
|
+
class << self
|
37
|
+
def load_from(path = File.join(DEFAULT_DIRECTORY_PATH, DEFAULT_DIRECTORY_NAME))
|
38
|
+
exists = File.exist?(path)
|
39
|
+
|
40
|
+
raise "no repository found #{path}" unless exists
|
41
|
+
|
42
|
+
new(dir: path)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def load_values_from_config_file
|
49
|
+
yml = YAML.load_file(File.join(file_path))
|
50
|
+
|
51
|
+
Repository::Config.new(file_path, yml)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "config_store"
|
4
|
+
|
5
|
+
# Initialize is an execution object which initializes a Repository on the
|
6
|
+
# filesystem
|
7
|
+
class Repository
|
8
|
+
class Initializer
|
9
|
+
INFO_FILE_SUFFIX = "devlogs.info.md"
|
10
|
+
LOG_TEMPLATE_FILE_NAME = "__log_template.erb.md"
|
11
|
+
|
12
|
+
# Creates a new devlogs repository at the provided path
|
13
|
+
def self.run(opts = {})
|
14
|
+
new_config = if opts[:dirpath]
|
15
|
+
Repository::ConfigStore.new(dir: opts[:dirpath])
|
16
|
+
else
|
17
|
+
Repository::ConfigStore.new
|
18
|
+
end
|
19
|
+
|
20
|
+
exists = File.exist?(new_config.file_path)
|
21
|
+
|
22
|
+
if exists && !opts[:force]
|
23
|
+
puts "Log repository already exists in #{new_config.file_path}. Aborting..."
|
24
|
+
raise RuntimeError
|
25
|
+
end
|
26
|
+
|
27
|
+
results = prompt_for_info
|
28
|
+
|
29
|
+
# Create the new_config directory
|
30
|
+
FileUtils.mkdir_p(new_config.dir)
|
31
|
+
|
32
|
+
# Create new_config file
|
33
|
+
File.open(new_config.file_path, "w") do |f|
|
34
|
+
f.write results.to_yaml
|
35
|
+
end
|
36
|
+
|
37
|
+
# Replace spaces in project name with underscores
|
38
|
+
sanitized_project_name = results[:name].gsub(/ /, "_").downcase
|
39
|
+
|
40
|
+
# Create the info file
|
41
|
+
info_file_name = "#{sanitized_project_name}.devlogs.info.md"
|
42
|
+
info_file = File.join(new_config.dir, info_file_name)
|
43
|
+
|
44
|
+
File.open(info_file, "w") do |f|
|
45
|
+
f.puts "# #{results[:name]}"
|
46
|
+
f.puts (results[:desc]).to_s
|
47
|
+
end
|
48
|
+
|
49
|
+
# Copy the default template file inside the gem into the repository
|
50
|
+
default_template_path = get_template_path
|
51
|
+
|
52
|
+
new_config_template_file_path = File.join(new_config.dir, Repository::ConfigStore::TEMPLATE_FILE)
|
53
|
+
|
54
|
+
FileUtils.cp(default_template_path, new_config_template_file_path)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Creates an interactive prompt for user input
|
58
|
+
#
|
59
|
+
# @returns [Hash]
|
60
|
+
def self.prompt_for_info
|
61
|
+
prompt = TTY::Prompt.new
|
62
|
+
|
63
|
+
prompt.collect do |_p|
|
64
|
+
# Project name
|
65
|
+
key(:name).ask("What is the project name?") do |q|
|
66
|
+
q.required true
|
67
|
+
end
|
68
|
+
|
69
|
+
# Project description
|
70
|
+
key(:desc).ask("What is the project description?") do |q|
|
71
|
+
q.required true
|
72
|
+
end
|
73
|
+
|
74
|
+
key(:mirror) do
|
75
|
+
key(:use_mirror).ask("Do you want to mirror these logs?", convert: :boolean)
|
76
|
+
key(:path).ask("Path to mirror directory: ")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Gets the template file path embedded in the gem from the library root
|
82
|
+
def self.get_template_path
|
83
|
+
File.join(Devlogs.lib_root, "templates", LOG_TEMPLATE_FILE_NAME)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rsync"
|
4
|
+
|
5
|
+
# FIXME: Create module
|
6
|
+
class Repository
|
7
|
+
#
|
8
|
+
# SyncManager is an abstraction class for managing any necessity to sync
|
9
|
+
# files on the file system using Rsync.
|
10
|
+
#
|
11
|
+
class SyncManager
|
12
|
+
#
|
13
|
+
# @param config_store [Repository::ConfigStore]
|
14
|
+
#
|
15
|
+
def initialize(config_store)
|
16
|
+
@config_store = config_store
|
17
|
+
end
|
18
|
+
|
19
|
+
# Run rsync with -a to copy directories recursively
|
20
|
+
|
21
|
+
# Use trailing slash to avoid sub-directory
|
22
|
+
# See rsync manual page
|
23
|
+
#
|
24
|
+
# @throws Error if sync fails
|
25
|
+
def run
|
26
|
+
dest_path = @config_store.values.mirror.path
|
27
|
+
src_path = config_store_path_with_trailing
|
28
|
+
|
29
|
+
runner.run("-av", src_path, dest_path) do |result|
|
30
|
+
if result.success?
|
31
|
+
puts "Mirror sync complete."
|
32
|
+
result.changes.each do |change|
|
33
|
+
puts "#{change.filename} (#{change.summary})"
|
34
|
+
end
|
35
|
+
else
|
36
|
+
raise "Failed to sync: #{result.error}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
#
|
44
|
+
# Utility method for getting access to the runner program
|
45
|
+
# @returns [Rsync]
|
46
|
+
#
|
47
|
+
def runner
|
48
|
+
@runner ||= Rsync
|
49
|
+
end
|
50
|
+
|
51
|
+
# Depending on the runner (Rsync) program,
|
52
|
+
# you may need a trailing slash on the directory path
|
53
|
+
#
|
54
|
+
# @returns [String]
|
55
|
+
def config_store_path_with_trailing
|
56
|
+
@config_store.dir[-1] == "/" ? @config_store.dir : @config_store.dir + "/"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/devlogs/repository.rb
CHANGED
@@ -2,55 +2,50 @@
|
|
2
2
|
|
3
3
|
require "fileutils"
|
4
4
|
require "tty-prompt"
|
5
|
-
require "yaml"
|
6
|
-
require "rsync"
|
7
5
|
require "pry"
|
8
6
|
require "time"
|
7
|
+
|
8
|
+
require_relative "repository/config_store"
|
9
9
|
require_relative "editor"
|
10
|
+
require_relative "repository/sync_manager"
|
11
|
+
require_relative "log_template"
|
10
12
|
|
11
13
|
# Repostiroy is an accessor object for the devlogs directory
|
12
14
|
class Repository
|
13
|
-
CONFIG_FILE = ".devlogs.config.yml"
|
14
|
-
|
15
|
-
# TODO: should be part of configuration
|
16
|
-
DEFAULT_LOG_SUFFIX = "devlogs.md"
|
17
|
-
DEFAULT_DIRECTORY_PATH = "."
|
18
|
-
DEFAULT_DIRECTORY_NAME = "_devlogs"
|
19
|
-
|
20
15
|
# Example: 11-22-2022_1343
|
21
|
-
|
22
|
-
|
16
|
+
TIME_FORMAT_FILE_PREFIX = "%m-%d-%Y__%kh%Mm"
|
17
|
+
|
18
|
+
LOG_FILE_SUFFIX = "log.md"
|
19
|
+
ISSUE_FILE_PREFIX = "iss"
|
23
20
|
|
24
21
|
VALID_DIRECTION = %i[asc desc].freeze
|
25
22
|
|
26
|
-
# Initializes a
|
27
|
-
# @param repo_config [Repository::Config]
|
23
|
+
# Initializes a .devlogs repository with the supplied configuration
|
28
24
|
#
|
29
|
-
def initialize(
|
30
|
-
@
|
25
|
+
def initialize(repo_config_store)
|
26
|
+
@config_store = repo_config_store
|
27
|
+
@repo_config = @config_store.values
|
31
28
|
end
|
32
29
|
|
33
|
-
# Creates a new
|
30
|
+
# Creates a new .devlogs entry for recording session completion
|
34
31
|
#
|
35
32
|
# @returns nil
|
36
33
|
def create
|
37
34
|
time = Time.new
|
38
|
-
|
35
|
+
time_prefix = time.strftime(TIME_FORMAT_FILE_PREFIX)
|
36
|
+
|
37
|
+
entry_file_name = "#{time_prefix}_#{LOG_FILE_SUFFIX}"
|
39
38
|
|
40
|
-
|
39
|
+
# FIXME: Need to figure out file path
|
40
|
+
entry_file_path = File.join(@config_store.dir, entry_file_name)
|
41
41
|
|
42
|
-
|
42
|
+
# FIXME: Need to figure out file path
|
43
|
+
template = LogTemplate.new(@config_store.template_file_path)
|
43
44
|
|
44
45
|
unless File.exist?(entry_file_path)
|
45
46
|
# Add default boiler plate if the file does not exist yet
|
46
47
|
File.open(entry_file_path, "w") do |f|
|
47
|
-
f.write
|
48
|
-
# #{time.strftime(DEFAULT_TIME_FORMAT_TEXT_ENTRY)}
|
49
|
-
Tags: #dev, #log
|
50
|
-
|
51
|
-
What did you do today?
|
52
|
-
|
53
|
-
ENDOFFILE
|
48
|
+
f.write template.render
|
54
49
|
end
|
55
50
|
end
|
56
51
|
|
@@ -64,30 +59,14 @@ class Repository
|
|
64
59
|
#
|
65
60
|
# @returns nil
|
66
61
|
def sync
|
67
|
-
if @
|
68
|
-
# Run rsync with -a to copy directories recursively
|
69
|
-
|
70
|
-
# Use trailing slash to avoid sub-directory
|
71
|
-
# See rsync manual page
|
72
|
-
|
73
|
-
Rsync.run("-av", @config.path(with_trailing: true), @config.mirror.path) do |result|
|
74
|
-
if result.success?
|
75
|
-
puts "Mirror sync complete."
|
76
|
-
result.changes.each do |change|
|
77
|
-
puts "#{change.filename} (#{change.summary})"
|
78
|
-
end
|
79
|
-
else
|
80
|
-
raise "Failed to sync: #{result.error}"
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
62
|
+
sync_manager.run if @repo_config.mirror?
|
84
63
|
end
|
85
64
|
|
86
65
|
# Lists the files in the repository
|
87
66
|
def ls(direction = :desc)
|
88
67
|
raise ArgumentError, "Must be one of: " + VALID_DIRECTION unless VALID_DIRECTION.include?(direction.to_sym)
|
89
68
|
|
90
|
-
Dir.glob(File.join(@
|
69
|
+
Dir.glob(File.join(@config_store.dir, "*_#{LOG_FILE_SUFFIX}")).sort_by do |fpath|
|
91
70
|
# The date is joined by two underscores to the suffix
|
92
71
|
date, = File.basename(fpath).split("__")
|
93
72
|
|
@@ -107,129 +86,16 @@ class Repository
|
|
107
86
|
#
|
108
87
|
# @returns [Repository]
|
109
88
|
#
|
110
|
-
def load(path = File.join(DEFAULT_DIRECTORY_PATH, DEFAULT_DIRECTORY_NAME))
|
111
|
-
|
112
|
-
|
113
|
-
raise "no repository found #{path}" unless exists
|
114
|
-
|
115
|
-
cfg = YAML.load_file(File.join(path, CONFIG_FILE))
|
116
|
-
|
117
|
-
cfg[:path] = path
|
89
|
+
def load(path = File.join(Repository::ConfigStore::DEFAULT_DIRECTORY_PATH, Repository::ConfigStore::DEFAULT_DIRECTORY_NAME))
|
90
|
+
store = Repository::ConfigStore.load_from(path)
|
118
91
|
|
119
|
-
|
120
|
-
|
121
|
-
new(repo_config)
|
92
|
+
new(store)
|
122
93
|
end
|
123
94
|
end
|
124
95
|
|
125
|
-
|
126
|
-
# in memory for access.
|
127
|
-
class Config
|
128
|
-
attr_reader :name, :description, :mirror, :path_value
|
129
|
-
|
130
|
-
# Configuration associated with the Mirror
|
131
|
-
MirrorConfig = Struct.new(:use_mirror, :path, keyword_init: true)
|
132
|
-
|
133
|
-
def initialize(name, desc, mirror, p)
|
134
|
-
@name = name
|
135
|
-
@description = desc
|
136
|
-
@mirror = MirrorConfig.new(mirror)
|
137
|
-
@path_value = p
|
138
|
-
end
|
139
|
-
|
140
|
-
# Returns whether or not the devlogs repository is configured to mirror
|
141
|
-
#
|
142
|
-
# @returns [Boolean]
|
143
|
-
def mirror?
|
144
|
-
@mirror.use_mirror
|
145
|
-
end
|
146
|
-
|
147
|
-
def path(with_trailing: false)
|
148
|
-
if with_trailing
|
149
|
-
@path_value[-1] == "/" ? @path_value : @path_value + "/"
|
150
|
-
else
|
151
|
-
@path_value
|
152
|
-
end
|
153
|
-
end
|
96
|
+
private
|
154
97
|
|
155
|
-
|
156
|
-
|
157
|
-
# @returns [Repository::Config]
|
158
|
-
def self.hydrate(cfg)
|
159
|
-
new(cfg[:name], cfg[:description], cfg[:mirror], cfg[:path])
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
|
-
# Initialize is an execution object which initializes a Repository on the
|
164
|
-
# filesystem
|
165
|
-
class Initialize
|
166
|
-
# Creates a new devlogs repository at the provided path
|
167
|
-
def self.run(opts = {}, path = File.join(DEFAULT_DIRECTORY_PATH, DEFAULT_DIRECTORY_NAME))
|
168
|
-
exists = File.exist?(path)
|
169
|
-
|
170
|
-
if exists && !opts[:force]
|
171
|
-
puts "Log repository already exists in #{path}. Aborting..."
|
172
|
-
raise RuntimeError
|
173
|
-
end
|
174
|
-
|
175
|
-
results = prompt_for_info
|
176
|
-
|
177
|
-
FileUtils.mkdir_p(path)
|
178
|
-
config_file = File.join(path, CONFIG_FILE)
|
179
|
-
|
180
|
-
# Replace spaces in project name with underscores
|
181
|
-
sanitized_project_name = results[:name].gsub(/ /, "_").downcase
|
182
|
-
|
183
|
-
info_file_name = "#{sanitized_project_name}_devlogs.info.md"
|
184
|
-
info_file = File.join(path, info_file_name)
|
185
|
-
|
186
|
-
# Create config file
|
187
|
-
File.open(config_file, "w") do |f|
|
188
|
-
f.write results.to_yaml
|
189
|
-
end
|
190
|
-
|
191
|
-
# Create the info file
|
192
|
-
File.open(info_file, "w") do |f|
|
193
|
-
f.puts "# #{results[:name]}"
|
194
|
-
f.puts (results[:desc]).to_s
|
195
|
-
end
|
196
|
-
|
197
|
-
# Git ignore if specified
|
198
|
-
if results[:gitignore]
|
199
|
-
gitignore = File.join(path, ".gitignore")
|
200
|
-
|
201
|
-
File.open(gitignore, "a") do |f|
|
202
|
-
f.puts DEFAULT_DIRECTORY_NAME
|
203
|
-
end
|
204
|
-
end
|
205
|
-
end
|
206
|
-
|
207
|
-
# Creates an interactive prompt for user input
|
208
|
-
#
|
209
|
-
# @returns [Hash]
|
210
|
-
def self.prompt_for_info
|
211
|
-
prompt = TTY::Prompt.new
|
212
|
-
|
213
|
-
prompt.collect do |_p|
|
214
|
-
# Project name
|
215
|
-
key(:name).ask("What is the project name?") do |q|
|
216
|
-
q.required true
|
217
|
-
end
|
218
|
-
|
219
|
-
# Project description
|
220
|
-
key(:desc).ask("What is the project description?") do |q|
|
221
|
-
q.required true
|
222
|
-
end
|
223
|
-
|
224
|
-
key(:mirror) do
|
225
|
-
key(:use_mirror).ask("Do you want to mirror these logs?", convert: :boolean)
|
226
|
-
key(:path).ask("Path to mirror directory: ")
|
227
|
-
end
|
228
|
-
|
229
|
-
key(:gitignore).ask("Do you want to gitignore the devlogs repository?") do |q|
|
230
|
-
q.required true
|
231
|
-
end
|
232
|
-
end
|
233
|
-
end
|
98
|
+
def sync_manager
|
99
|
+
@sync_manager ||= Repository::SyncManager.new(@config_store)
|
234
100
|
end
|
235
101
|
end
|
data/lib/devlogs/version.rb
CHANGED
data/lib/devlogs.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: devlogs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- aquaflamingo
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-09-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rsync
|
@@ -86,6 +86,7 @@ files:
|
|
86
86
|
- Gemfile
|
87
87
|
- Gemfile.lock
|
88
88
|
- LICENSE.txt
|
89
|
+
- Makefile
|
89
90
|
- README.md
|
90
91
|
- Rakefile
|
91
92
|
- bin/console
|
@@ -96,7 +97,17 @@ files:
|
|
96
97
|
- lib/devlogs.rb
|
97
98
|
- lib/devlogs/cli.rb
|
98
99
|
- lib/devlogs/editor.rb
|
100
|
+
- lib/devlogs/executable.rb
|
101
|
+
- lib/devlogs/gem.rb
|
102
|
+
- lib/devlogs/log_template.rb
|
103
|
+
- lib/devlogs/pager.rb
|
104
|
+
- lib/devlogs/prompt_utils.rb
|
99
105
|
- lib/devlogs/repository.rb
|
106
|
+
- lib/devlogs/repository/config.rb
|
107
|
+
- lib/devlogs/repository/config_store.rb
|
108
|
+
- lib/devlogs/repository/initializer.rb
|
109
|
+
- lib/devlogs/repository/sync_manager.rb
|
110
|
+
- lib/devlogs/templates/__log_template.erb.md
|
100
111
|
- lib/devlogs/version.rb
|
101
112
|
homepage: http://github.com/aquaflamingo/devlogs
|
102
113
|
licenses:
|