git-fit 0.4.6 → 0.5.1
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/lib/git-fit.rb +4 -0
- data/lib/git_fit/cli/import_cli.rb +71 -71
- data/lib/git_fit/cli/install_cli.rb +22 -0
- data/lib/git_fit/cli/remote_cli.rb +68 -0
- data/lib/git_fit/cli.rb +6 -0
- data/lib/git_fit/db/connection.rb +5 -1
- data/lib/git_fit/db/rebuild.rb +48 -0
- data/lib/git_fit/install/actions.rb +90 -0
- data/lib/git_fit/version.rb +1 -1
- metadata +5 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 634af580a8c63dc636583e0835c76433175460d85acd667900e795076d8fc2f0
|
|
4
|
+
data.tar.gz: a1c3b2a10ac1bf9c9ff9dae7e064adcabe3be881c335297bcce9604decc2b480
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c543617f7fd0e9abe52f1ccf499f185162299029298fe4e2da03a00de5f17fc80a977ddf4166ff2f4406ef6abd141a7336b98b81f4483c679ea8fe2fa0128118
|
|
7
|
+
data.tar.gz: 8c2a12124b055b3f7a510f874bff7e2e76c96b6446e1b9b5638838e7e44c859ce4995db785fee1c2a955ba3e3a72eb864a23e84260dfb793d7bfb89f678a4dc1
|
data/lib/git-fit.rb
CHANGED
|
@@ -57,10 +57,14 @@ require_relative "git_fit/privacy/polyline_filter"
|
|
|
57
57
|
require_relative "git_fit/import"
|
|
58
58
|
require_relative "git_fit/sync/base"
|
|
59
59
|
require_relative "git_fit/sync/strava"
|
|
60
|
+
require_relative "git_fit/db/rebuild"
|
|
60
61
|
require_relative "git_fit/db/connection"
|
|
61
62
|
require_relative "git_fit/db/activity"
|
|
62
63
|
require_relative "git_fit/export"
|
|
63
64
|
require_relative "git_fit/db/cli"
|
|
65
|
+
require_relative "git_fit/install/actions"
|
|
66
|
+
require_relative "git_fit/cli/install_cli"
|
|
67
|
+
require_relative "git_fit/cli/remote_cli"
|
|
64
68
|
require_relative "git_fit/cli/export"
|
|
65
69
|
require_relative "git_fit/cli/import_cli"
|
|
66
70
|
require_relative "git_fit/cli"
|
|
@@ -78,91 +78,91 @@ module GitFit
|
|
|
78
78
|
say_status :error, "gpx: #{e.message}", :red
|
|
79
79
|
0
|
|
80
80
|
end
|
|
81
|
+
end
|
|
81
82
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
83
|
+
desc "tcx", "Import TCX files from data/import/tcx/"
|
|
84
|
+
option :activity, type: :array, desc: "Filter by sport type", aliases: "--act"
|
|
85
|
+
def tcx
|
|
86
|
+
config = GitFit::Config.new
|
|
87
|
+
db = GitFit::DB::Connection.new(config.db_path).db
|
|
88
|
+
filter = options[:activity]&.map(&:strip)&.map(&:downcase)
|
|
89
|
+
time_budget = config.dig("sync", "time_budget")
|
|
89
90
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
91
|
+
say_status :import, "tcx", :green
|
|
92
|
+
adapter = GitFit::Import::LocalFile.new(
|
|
93
|
+
config: {}, db:, activity_filter: filter,
|
|
94
|
+
time_budget: time_budget,
|
|
95
|
+
sources: ["tcx"]
|
|
96
|
+
)
|
|
96
97
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
end
|
|
104
|
-
count
|
|
105
|
-
rescue => e
|
|
106
|
-
say_status :error, "tcx: #{e.message}", :red
|
|
107
|
-
0
|
|
98
|
+
begin
|
|
99
|
+
count = adapter.call
|
|
100
|
+
if count == 0
|
|
101
|
+
say_status :warn, "tcx: 0 activities (already imported or none found)", :yellow
|
|
102
|
+
else
|
|
103
|
+
say_status :done, "tcx: #{count} activities", :green
|
|
108
104
|
end
|
|
105
|
+
count
|
|
106
|
+
rescue => e
|
|
107
|
+
say_status :error, "tcx: #{e.message}", :red
|
|
108
|
+
0
|
|
109
109
|
end
|
|
110
|
+
end
|
|
110
111
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
112
|
+
desc "fit", "Import FIT files from data/import/fit/"
|
|
113
|
+
option :activity, type: :array, desc: "Filter by sport type", aliases: "--act"
|
|
114
|
+
def fit
|
|
115
|
+
config = GitFit::Config.new
|
|
116
|
+
db = GitFit::DB::Connection.new(config.db_path).db
|
|
117
|
+
filter = options[:activity]&.map(&:strip)&.map(&:downcase)
|
|
118
|
+
time_budget = config.dig("sync", "time_budget")
|
|
118
119
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
120
|
+
say_status :import, "fit", :green
|
|
121
|
+
adapter = GitFit::Import::LocalFile.new(
|
|
122
|
+
config: {}, db:, activity_filter: filter,
|
|
123
|
+
time_budget: time_budget,
|
|
124
|
+
sources: ["fit"]
|
|
125
|
+
)
|
|
125
126
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
end
|
|
133
|
-
count
|
|
134
|
-
rescue => e
|
|
135
|
-
say_status :error, "fit: #{e.message}", :red
|
|
136
|
-
0
|
|
127
|
+
begin
|
|
128
|
+
count = adapter.call
|
|
129
|
+
if count == 0
|
|
130
|
+
say_status :warn, "fit: 0 activities (already imported or none found)", :yellow
|
|
131
|
+
else
|
|
132
|
+
say_status :done, "fit: #{count} activities", :green
|
|
137
133
|
end
|
|
134
|
+
count
|
|
135
|
+
rescue => e
|
|
136
|
+
say_status :error, "fit: #{e.message}", :red
|
|
137
|
+
0
|
|
138
138
|
end
|
|
139
|
+
end
|
|
139
140
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
141
|
+
desc "apple_health", "Import from Apple Health export.zip"
|
|
142
|
+
option :activity, type: :array, desc: "Filter by sport type", aliases: "--act"
|
|
143
|
+
def apple_health
|
|
144
|
+
config = GitFit::Config.new
|
|
145
|
+
db = GitFit::DB::Connection.new(config.db_path).db
|
|
146
|
+
filter = options[:activity]&.map(&:strip)&.map(&:downcase)
|
|
147
|
+
time_budget = config.dig("sync", "time_budget")
|
|
147
148
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
149
|
+
say_status :import, "apple_health", :green
|
|
150
|
+
adapter = GitFit::Import::AppleHealth.new(
|
|
151
|
+
config: {}, db:, activity_filter: filter,
|
|
152
|
+
time_budget: time_budget
|
|
153
|
+
)
|
|
153
154
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
end
|
|
161
|
-
count
|
|
162
|
-
rescue => e
|
|
163
|
-
say_status :error, "apple_health: #{e.message}", :red
|
|
164
|
-
0
|
|
155
|
+
begin
|
|
156
|
+
count = adapter.call
|
|
157
|
+
if count == 0
|
|
158
|
+
say_status :warn, "apple_health: 0 activities (already imported or none found)", :yellow
|
|
159
|
+
else
|
|
160
|
+
say_status :done, "apple_health: #{count} activities", :green
|
|
165
161
|
end
|
|
162
|
+
count
|
|
163
|
+
rescue => e
|
|
164
|
+
say_status :error, "apple_health: #{e.message}", :red
|
|
165
|
+
0
|
|
166
166
|
end
|
|
167
167
|
end
|
|
168
168
|
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require "thor"
|
|
2
|
+
require "fileutils"
|
|
3
|
+
|
|
4
|
+
module GitFit
|
|
5
|
+
class InstallCLI < Thor
|
|
6
|
+
desc "actions", "Install GitHub Actions (db-cache restore/save)"
|
|
7
|
+
def actions
|
|
8
|
+
base = ".github/actions/db-cache"
|
|
9
|
+
restore_path = File.join(base, "restore", "action.yml")
|
|
10
|
+
save_path = File.join(base, "save", "action.yml")
|
|
11
|
+
|
|
12
|
+
FileUtils.mkdir_p(File.dirname(restore_path))
|
|
13
|
+
FileUtils.mkdir_p(File.dirname(save_path))
|
|
14
|
+
|
|
15
|
+
File.write(restore_path, GitFit::Install::Actions::RESTORE_ACTION)
|
|
16
|
+
File.write(save_path, GitFit::Install::Actions::SAVE_ACTION)
|
|
17
|
+
|
|
18
|
+
say_status :created, restore_path, :green
|
|
19
|
+
say_status :created, save_path, :green
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
require "thor"
|
|
2
|
+
require "open3"
|
|
3
|
+
|
|
4
|
+
module GitFit
|
|
5
|
+
module RemoteHelpers
|
|
6
|
+
def detect_repo(url = nil)
|
|
7
|
+
url ||= `git remote get-url origin`.chomp
|
|
8
|
+
url.sub(%r{.*github\.com[:/]}, "").sub(/\.git$/, "")
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def gh(*args)
|
|
12
|
+
repo = options[:repo] || detect_repo
|
|
13
|
+
out, err, st = Open3.capture3("gh", *args.map(&:to_s), "-R", repo)
|
|
14
|
+
raise "gh: #{err.strip}" unless st.success?
|
|
15
|
+
out
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class RemoteSecretCLI < Thor
|
|
20
|
+
include RemoteHelpers
|
|
21
|
+
|
|
22
|
+
class_option :repo, type: :string, desc: "GitHub repo (owner/repo)"
|
|
23
|
+
|
|
24
|
+
desc "list", "List secrets"
|
|
25
|
+
def list
|
|
26
|
+
puts gh("secret", "list")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
desc "set KEY", "Set a secret (reads value from stdin or prompt)"
|
|
30
|
+
def set(key)
|
|
31
|
+
puts gh("secret", "set", key)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
desc "delete KEY", "Delete a secret"
|
|
35
|
+
def delete(key)
|
|
36
|
+
puts gh("secret", "delete", key)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class RemoteConfigCLI < Thor
|
|
41
|
+
include RemoteHelpers
|
|
42
|
+
|
|
43
|
+
class_option :repo, type: :string, desc: "GitHub repo (owner/repo)"
|
|
44
|
+
|
|
45
|
+
desc "list", "List variables"
|
|
46
|
+
def list
|
|
47
|
+
puts gh("variable", "list")
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
desc "set KEY VALUE", "Set a variable"
|
|
51
|
+
def set(key, value)
|
|
52
|
+
puts gh("variable", "set", key, "--body", value)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
desc "get KEY", "Get a variable value"
|
|
56
|
+
def get(key)
|
|
57
|
+
puts gh("variable", "get", key)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
class RemoteCLI < Thor
|
|
62
|
+
desc "secret SUBCOMMAND", "Manage GitHub Secrets"
|
|
63
|
+
subcommand "secret", RemoteSecretCLI
|
|
64
|
+
|
|
65
|
+
desc "config SUBCOMMAND", "Manage GitHub Variables"
|
|
66
|
+
subcommand "config", RemoteConfigCLI
|
|
67
|
+
end
|
|
68
|
+
end
|
data/lib/git_fit/cli.rb
CHANGED
|
@@ -61,6 +61,12 @@ module GitFit
|
|
|
61
61
|
say_status :error, "Parse failed: #{e.message}", :red
|
|
62
62
|
end
|
|
63
63
|
|
|
64
|
+
desc "remote SUBCOMMAND", "Manage GitHub remote secrets and config"
|
|
65
|
+
subcommand "remote", RemoteCLI
|
|
66
|
+
|
|
67
|
+
desc "install SUBCOMMAND", "Install project assets (actions)"
|
|
68
|
+
subcommand "install", InstallCLI
|
|
69
|
+
|
|
64
70
|
desc "init", "Generate config.yml"
|
|
65
71
|
option :output, type: :string, desc: "Output path", aliases: "-o"
|
|
66
72
|
def init
|
|
@@ -20,7 +20,11 @@ module GitFit
|
|
|
20
20
|
|
|
21
21
|
def run_migrations
|
|
22
22
|
Sequel.extension :migration
|
|
23
|
-
|
|
23
|
+
path = File.expand_path("../../../db/migrations", __dir__)
|
|
24
|
+
Sequel::Migrator.run(@db, path)
|
|
25
|
+
rescue Sequel::Migrator::Error
|
|
26
|
+
GitFit::DB::Rebuild.call(@db, @db.opts[:database], path)
|
|
27
|
+
Sequel::Migrator.run(@db, path)
|
|
24
28
|
end
|
|
25
29
|
end
|
|
26
30
|
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
require "fileutils"
|
|
2
|
+
|
|
3
|
+
module GitFit
|
|
4
|
+
module DB
|
|
5
|
+
class RebuildError < StandardError; end
|
|
6
|
+
|
|
7
|
+
module Rebuild
|
|
8
|
+
def self.call(db, db_path, migrations_path)
|
|
9
|
+
backup = "#{db_path}.rebuild_bak"
|
|
10
|
+
FileUtils.cp(db_path, backup)
|
|
11
|
+
|
|
12
|
+
tables = db.tables - [:schema_migrations]
|
|
13
|
+
data = {}
|
|
14
|
+
tables.each { |t| data[t] = export_table(db, t) }
|
|
15
|
+
|
|
16
|
+
db.run("PRAGMA foreign_keys = OFF")
|
|
17
|
+
tables.each { |t| db.drop_table(t) }
|
|
18
|
+
|
|
19
|
+
Sequel.extension :migration
|
|
20
|
+
Sequel::Migrator.run(db, migrations_path)
|
|
21
|
+
|
|
22
|
+
new_tables = db.tables.to_set
|
|
23
|
+
data.each do |table, rows|
|
|
24
|
+
next unless new_tables.include?(table)
|
|
25
|
+
import_table(db, table, rows) if rows.any?
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
warn "Rebuilt #{db_path}: #{data.values.sum(&:size)} rows from #{data.size} tables"
|
|
29
|
+
rescue => e
|
|
30
|
+
if backup && File.exist?(backup)
|
|
31
|
+
FileUtils.cp(backup, db_path)
|
|
32
|
+
end
|
|
33
|
+
raise RebuildError, "Rebuild failed: #{e.message}"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.export_table(db, table)
|
|
37
|
+
db[table].all.map { |r| r.except(:id) }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def self.import_table(db, table, rows)
|
|
41
|
+
return if rows.empty?
|
|
42
|
+
target = db[table].columns - [:id]
|
|
43
|
+
filtered = rows.map { |r| r.select { |k, _| target.include?(k) } }
|
|
44
|
+
db[table].multi_insert(filtered)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
module GitFit
|
|
2
|
+
module Install
|
|
3
|
+
module Actions
|
|
4
|
+
RESTORE_ACTION = <<~RESTORE_ACTION_YAML
|
|
5
|
+
name: DB Cache Restore
|
|
6
|
+
description: Restore SQLite DB with checksum verification
|
|
7
|
+
inputs:
|
|
8
|
+
path:
|
|
9
|
+
required: false
|
|
10
|
+
default: db/fit.sqlite3
|
|
11
|
+
key-prefix:
|
|
12
|
+
required: false
|
|
13
|
+
default: git-fit-db
|
|
14
|
+
runs:
|
|
15
|
+
using: composite
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/cache/restore@v4
|
|
18
|
+
id: restore-db
|
|
19
|
+
with:
|
|
20
|
+
path: |-
|
|
21
|
+
${{ inputs.path }}
|
|
22
|
+
${{ inputs.path }}.checksum
|
|
23
|
+
key: ${{ inputs.key-prefix }}-v1-${{ github.run_id }}
|
|
24
|
+
restore-keys: ${{ inputs.key-prefix }}-v1-
|
|
25
|
+
|
|
26
|
+
- name: Verify checksum
|
|
27
|
+
shell: bash
|
|
28
|
+
run: |
|
|
29
|
+
db="${{ inputs.path }}"
|
|
30
|
+
ck="${db}.checksum"
|
|
31
|
+
if [ ! -f "$ck" ]; then
|
|
32
|
+
echo "No checksum file — first run or cache miss, skipping verify"
|
|
33
|
+
exit 0
|
|
34
|
+
fi
|
|
35
|
+
if ! sha256sum --check "$ck" --quiet 2>/dev/null; then
|
|
36
|
+
echo "::warning::Checksum mismatch — $db may be corrupted"
|
|
37
|
+
echo "Removing stale files"
|
|
38
|
+
rm -f "$db" "$ck"
|
|
39
|
+
else
|
|
40
|
+
echo "Checksum OK — $db is intact"
|
|
41
|
+
fi
|
|
42
|
+
RESTORE_ACTION_YAML
|
|
43
|
+
|
|
44
|
+
SAVE_ACTION = <<~SAVE_ACTION_YAML
|
|
45
|
+
name: DB Cache Save
|
|
46
|
+
description: Save SQLite DB with checksum, skip if unchanged
|
|
47
|
+
inputs:
|
|
48
|
+
path:
|
|
49
|
+
required: false
|
|
50
|
+
default: db/fit.sqlite3
|
|
51
|
+
key-prefix:
|
|
52
|
+
required: false
|
|
53
|
+
default: git-fit-db
|
|
54
|
+
outputs:
|
|
55
|
+
saved:
|
|
56
|
+
description: "true if cache was updated, false if unchanged"
|
|
57
|
+
value: ${{ steps.detect.outputs.saved }}
|
|
58
|
+
runs:
|
|
59
|
+
using: composite
|
|
60
|
+
steps:
|
|
61
|
+
- name: Detect changes
|
|
62
|
+
id: detect
|
|
63
|
+
shell: bash
|
|
64
|
+
run: |
|
|
65
|
+
db="${{ inputs.path }}"
|
|
66
|
+
ck="${db}.checksum"
|
|
67
|
+
new_sum=$(sha256sum "$db" 2>/dev/null || echo "")
|
|
68
|
+
if [ -z "$new_sum" ]; then
|
|
69
|
+
echo "saved=false" >> "$GITHUB_OUTPUT"
|
|
70
|
+
exit 0
|
|
71
|
+
fi
|
|
72
|
+
if [ -f "$ck" ] && sha256sum --check "$ck" --quiet 2>/dev/null; then
|
|
73
|
+
echo "DB unchanged — skip save"
|
|
74
|
+
echo "saved=false" >> "$GITHUB_OUTPUT"
|
|
75
|
+
exit 0
|
|
76
|
+
fi
|
|
77
|
+
echo "$new_sum" > "$ck"
|
|
78
|
+
echo "saved=true" >> "$GITHUB_OUTPUT"
|
|
79
|
+
|
|
80
|
+
- uses: actions/cache/save@v4
|
|
81
|
+
if: steps.detect.outputs.saved == 'true'
|
|
82
|
+
with:
|
|
83
|
+
path: |-
|
|
84
|
+
${{ inputs.path }}
|
|
85
|
+
${{ inputs.path }}.checksum
|
|
86
|
+
key: ${{ inputs.key-prefix }}-v1-${{ github.run_id }}
|
|
87
|
+
SAVE_ACTION_YAML
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
data/lib/git_fit/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: git-fit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Lax
|
|
@@ -135,11 +135,14 @@ files:
|
|
|
135
135
|
- lib/git_fit/cli.rb
|
|
136
136
|
- lib/git_fit/cli/export.rb
|
|
137
137
|
- lib/git_fit/cli/import_cli.rb
|
|
138
|
+
- lib/git_fit/cli/install_cli.rb
|
|
139
|
+
- lib/git_fit/cli/remote_cli.rb
|
|
138
140
|
- lib/git_fit/config.rb
|
|
139
141
|
- lib/git_fit/config_template.rb
|
|
140
142
|
- lib/git_fit/db/activity.rb
|
|
141
143
|
- lib/git_fit/db/cli.rb
|
|
142
144
|
- lib/git_fit/db/connection.rb
|
|
145
|
+
- lib/git_fit/db/rebuild.rb
|
|
143
146
|
- lib/git_fit/export.rb
|
|
144
147
|
- lib/git_fit/export/defaults.rb
|
|
145
148
|
- lib/git_fit/export/json.rb
|
|
@@ -151,6 +154,7 @@ files:
|
|
|
151
154
|
- lib/git_fit/import.rb
|
|
152
155
|
- lib/git_fit/import/apple_health.rb
|
|
153
156
|
- lib/git_fit/import/local_file.rb
|
|
157
|
+
- lib/git_fit/install/actions.rb
|
|
154
158
|
- lib/git_fit/parser.rb
|
|
155
159
|
- lib/git_fit/parser/base.rb
|
|
156
160
|
- lib/git_fit/parser/fit.rb
|