git-fit 0.0.0 → 0.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 741b14cfd4c4250649d0c6998d6fe104b85fd9e35e82a03c7d5e99ee67e0985b
4
- data.tar.gz: bac18a446b4bbc0cd52d4f2230d2513e53fc617f94a662c2b27264a385c37639
3
+ metadata.gz: e868be2f4295bf56a1af4e653dcadb26bca9c67a3f4cf42e8c5822b55ae380e6
4
+ data.tar.gz: 49470e6b69e829a484e7347a44cb48241f9366a6cb2a345023da903e4045d00f
5
5
  SHA512:
6
- metadata.gz: 32d955e360b245b13cf24790e99418b41876fbebbb6f7863fd96e932d8ad778740a464bdc1fc8adfc2ac5f272fd0536201a770099b3af573178707c5f14c06d4
7
- data.tar.gz: adec61b2013270f90bd3703ffaaa3722d3d1664759d1446fab073adc0271d85077eb6436ea92ce24eae437b4e75d1dde53fddbe2d17aae72d4fb502fc61aa076
6
+ metadata.gz: d5a45ab91987725f37a00dadf6e16f8c2c45a4258a9bde275bebc8203c12bd7376e9d4605dcd87c1f5432c735c50d9ac30e42deb18b4d92611b1a90418834b7f
7
+ data.tar.gz: 2ce6cd5123f4d55ff5f8316c5cdd6c912dc0be3721db63b5a844c149fa38743792a5d2e4c3d46feb3e2d4d23562a7f0c6f04f17d4f9c29728384eb2647f49e07
data/exe/git-fit ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative "../lib/git-fit"
4
+
5
+ GitFit::CLI.start(ARGV)
data/lib/git-fit.rb CHANGED
@@ -1 +1,8 @@
1
+ require "thor"
2
+ require "yaml"
3
+ require "fileutils"
4
+
1
5
  require_relative "git_fit/version"
6
+ require_relative "git_fit/config"
7
+ require_relative "git_fit/config_template"
8
+ require_relative "git_fit/cli"
@@ -0,0 +1,61 @@
1
+ require "thor"
2
+
3
+ module GitFit
4
+ class CLI < Thor
5
+ package_name "git-fit"
6
+
7
+ class_option :config, type: :string, desc: "Config file path", aliases: "-c"
8
+
9
+ def self.exit_on_failure?
10
+ true
11
+ end
12
+
13
+ desc "version", "Show version"
14
+ def version
15
+ puts "git-fit v#{GitFit::VERSION}"
16
+ end
17
+
18
+ desc "init", "Generate config.yml"
19
+ option :output, type: :string, desc: "Output path", aliases: "-o"
20
+ def init
21
+ path = options[:output] || "config/config.yml"
22
+ FileUtils.mkdir_p(File.dirname(path))
23
+
24
+ if File.exist?(path)
25
+ existing = YAML.safe_load(File.read(path)) || {}
26
+ merged = deep_merge(ConfigTemplate.hash, existing)
27
+ File.write(path, YAML.dump(merged))
28
+ say_status :merged, path, :green
29
+ else
30
+ File.write(path, ConfigTemplate.generate)
31
+ say_status :created, path, :green
32
+ end
33
+ end
34
+
35
+ no_commands do
36
+ def git_fit_config
37
+ @git_fit_config ||= GitFit::Config.new(options[:config])
38
+ end
39
+
40
+ def deep_merge(template, existing)
41
+ result = deep_dup(template)
42
+ existing.each do |key, val|
43
+ if val.is_a?(Hash) && result[key].is_a?(Hash)
44
+ result[key] = deep_merge(result[key], val)
45
+ else
46
+ result[key] = val
47
+ end
48
+ end
49
+ result
50
+ end
51
+
52
+ def deep_dup(obj)
53
+ case obj
54
+ when Hash then obj.each_with_object({}) { |(k, v), h| h[k] = deep_dup(v) }
55
+ when Array then obj.map { |v| deep_dup(v) }
56
+ else obj
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,101 @@
1
+ require "yaml"
2
+
3
+ module GitFit
4
+ # 三优先级: env var > YAML > DEFAULTS
5
+ class Config
6
+ DEFAULTS = {
7
+ "database" => { "path" => "db/fit.sqlite3" },
8
+ "sync" => {},
9
+ "export" => {
10
+ "json" => { "path" => "site/activities.json" },
11
+ "svg" => { "dir" => "site/svg" },
12
+ "csv" => { "path" => "site/workouts.csv" },
13
+ "gpx" => { "path" => "site/gpx_out" }
14
+ },
15
+ "privacy" => {
16
+ "start_end_range" => 200
17
+ },
18
+ "system" => {
19
+ "units" => "metric",
20
+ "log_level" => "info",
21
+ "timezone" => "Asia/Shanghai"
22
+ }
23
+ }.freeze
24
+
25
+ DEFAULT_PATH = "config/config.yml"
26
+ KNOWN_TOP_KEYS = %w[database sync export privacy system].freeze
27
+
28
+ def initialize(path = nil)
29
+ @data = deep_dup(DEFAULTS)
30
+ load_file(path) if path
31
+ load_file(DEFAULT_PATH) if !path && File.exist?(DEFAULT_PATH)
32
+ load_env
33
+ validate!
34
+ end
35
+
36
+ def [](key)
37
+ @data[key]
38
+ end
39
+
40
+ def dig(*keys)
41
+ @data.dig(*keys)
42
+ end
43
+
44
+ def sync_config(source)
45
+ @data.dig("sync", source) || {}
46
+ end
47
+
48
+ def privacy_config
49
+ @data["privacy"] || {}
50
+ end
51
+
52
+ def export_config
53
+ @data["export"] || {}
54
+ end
55
+
56
+ def db_path
57
+ @data.dig("database", "path") || DEFAULTS["database"]["path"]
58
+ end
59
+
60
+ private
61
+
62
+ # v0.1.0: 仅支持 DATABASE_PATH 覆盖
63
+ # v0.1.1+: 引入 register_config 注册机制
64
+ def load_env
65
+ @data["database"]["path"] = ENV["GIT_FIT_DATABASE_PATH"] if ENV["GIT_FIT_DATABASE_PATH"]
66
+ end
67
+
68
+ def validate!
69
+ @data.each_key do |k|
70
+ next if k.empty? || KNOWN_TOP_KEYS.include?(k)
71
+ warn "Config: unknown top-level key '#{k}'"
72
+ end
73
+ end
74
+
75
+ def load_file(path)
76
+ yaml = YAML.safe_load(File.read(path)) || {}
77
+ deep_merge!(@data, yaml)
78
+ rescue => e
79
+ warn "Config: failed to load #{path}: #{e.message}"
80
+ end
81
+
82
+ def deep_merge!(target, source)
83
+ source.each do |key, val|
84
+ if val.is_a?(Hash) && target[key].is_a?(Hash)
85
+ deep_merge!(target[key], val)
86
+ else
87
+ target[key] = deep_dup(val)
88
+ end
89
+ end
90
+ target
91
+ end
92
+
93
+ def deep_dup(obj)
94
+ case obj
95
+ when Hash then obj.each_with_object({}) { |(k, v), h| h[k] = deep_dup(v) }
96
+ when Array then obj.map { |v| deep_dup(v) }
97
+ else obj
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,44 @@
1
+ module GitFit
2
+ class ConfigTemplate
3
+ def self.generate
4
+ <<~YAML
5
+ ---
6
+ database:
7
+ path: db/fit.sqlite3
8
+
9
+ sync:
10
+ # strava:
11
+ # client_id: ""
12
+ # refresh_token: ""
13
+
14
+ # garmin:
15
+ # email: ""
16
+ # password: ""
17
+
18
+ # keep:
19
+ # phone: ""
20
+ # password: ""
21
+
22
+ # igpsport:
23
+ # phone: ""
24
+ # password: ""
25
+
26
+ export:
27
+ json:
28
+ path: site/activities.json
29
+
30
+ privacy:
31
+ start_end_range: 200
32
+
33
+ system:
34
+ units: metric
35
+ log_level: info
36
+ timezone: Asia/Shanghai
37
+ YAML
38
+ end
39
+
40
+ def self.hash
41
+ YAML.safe_load(generate) || {}
42
+ end
43
+ end
44
+ end
@@ -1,3 +1,3 @@
1
1
  module GitFit
2
- VERSION = "0.0.0"
2
+ VERSION = "0.1.0"
3
3
  end
metadata CHANGED
@@ -1,23 +1,42 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: git-fit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lax
8
8
  bindir: exe
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
- dependencies: []
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: thor
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '1.3'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '1.3'
12
26
  description: A git extension CLI for aggregating, managing, and exporting fitness
13
27
  activity data from multiple sources (Garmin, Strava, Keep, etc.)
14
28
  email:
15
29
  - lax@liulantao.com
16
- executables: []
30
+ executables:
31
+ - git-fit
17
32
  extensions: []
18
33
  extra_rdoc_files: []
19
34
  files:
35
+ - exe/git-fit
20
36
  - lib/git-fit.rb
37
+ - lib/git_fit/cli.rb
38
+ - lib/git_fit/config.rb
39
+ - lib/git_fit/config_template.rb
21
40
  - lib/git_fit/version.rb
22
41
  homepage: https://github.com/Lax/git-fit
23
42
  licenses: