git-fit 0.4.6 → 0.5.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: 8bf1cd02058a384245ee15ef6471ae7847c3d104ce9774ad1c05c78d21cb2313
4
- data.tar.gz: 7d412258ebe13b65f7f35831e6b40f4aa76fd01377b9dfa7076770c147da6bcf
3
+ metadata.gz: 565aea6097483469ad19f905c14526df7fc9acdf8490c36f5b6edc3664395778
4
+ data.tar.gz: fdaf45ded02748b399694745b49abc206044c6504a3ecb6767abc4269f15e07d
5
5
  SHA512:
6
- metadata.gz: 82f3d058175b4a66b4e852ad42cccc3a119fe2e980a89179d9f21e19236dc1fec047e7a074db6014351f39c5080f61609759c7456b75d1aa2c0ac8e4c5861c35
7
- data.tar.gz: 7d43fd945b988ffec45bd88879ce470345320a223afe82d0354a09302adf453c66dbf6aea8da78ec9c6c022e701a985b958164bfa691073efd67a79e814f90ad
6
+ metadata.gz: 2c7213a9ef0e3c1151601d70cac666c91e32b545cfbf18c81882eccfc6f68b54ada354fec65f1eba1ce05ddada2e54ca7f9e23f5cef75ebbf59dc16e1c99e3b7
7
+ data.tar.gz: 2c2df21e495f16a25ff000992325012f8ff80acdb1c255d416749648e102dbb64d1ccd041cbc05bc68ba927a6cecd34fbac0b1b511d238e36f445169fcfd7998
data/lib/git-fit.rb CHANGED
@@ -57,10 +57,13 @@ 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"
64
67
  require_relative "git_fit/cli/export"
65
68
  require_relative "git_fit/cli/import_cli"
66
69
  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
- desc "tcx", "Import TCX files from data/import/tcx/"
83
- option :activity, type: :array, desc: "Filter by sport type", aliases: "--act"
84
- def tcx
85
- config = GitFit::Config.new
86
- db = GitFit::DB::Connection.new(config.db_path).db
87
- filter = options[:activity]&.map(&:strip)&.map(&:downcase)
88
- time_budget = config.dig("sync", "time_budget")
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
- say_status :import, "tcx", :green
91
- adapter = GitFit::Import::LocalFile.new(
92
- config: {}, db:, activity_filter: filter,
93
- time_budget: time_budget,
94
- sources: ["tcx"]
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
- begin
98
- count = adapter.call
99
- if count == 0
100
- say_status :warn, "tcx: 0 activities (already imported or none found)", :yellow
101
- else
102
- say_status :done, "tcx: #{count} activities", :green
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
- desc "fit", "Import FIT files from data/import/fit/"
112
- option :activity, type: :array, desc: "Filter by sport type", aliases: "--act"
113
- def fit
114
- config = GitFit::Config.new
115
- db = GitFit::DB::Connection.new(config.db_path).db
116
- filter = options[:activity]&.map(&:strip)&.map(&:downcase)
117
- time_budget = config.dig("sync", "time_budget")
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
- say_status :import, "fit", :green
120
- adapter = GitFit::Import::LocalFile.new(
121
- config: {}, db:, activity_filter: filter,
122
- time_budget: time_budget,
123
- sources: ["fit"]
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
- begin
127
- count = adapter.call
128
- if count == 0
129
- say_status :warn, "fit: 0 activities (already imported or none found)", :yellow
130
- else
131
- say_status :done, "fit: #{count} activities", :green
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
- desc "apple_health", "Import from Apple Health export.zip"
141
- option :activity, type: :array, desc: "Filter by sport type", aliases: "--act"
142
- def apple_health
143
- config = GitFit::Config.new
144
- db = GitFit::DB::Connection.new(config.db_path).db
145
- filter = options[:activity]&.map(&:strip)&.map(&:downcase)
146
- time_budget = config.dig("sync", "time_budget")
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
- say_status :import, "apple_health", :green
149
- adapter = GitFit::Import::AppleHealth.new(
150
- config: {}, db:, activity_filter: filter,
151
- time_budget: time_budget
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
- begin
155
- count = adapter.call
156
- if count == 0
157
- say_status :warn, "apple_health: 0 activities (already imported or none found)", :yellow
158
- else
159
- say_status :done, "apple_health: #{count} activities", :green
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
data/lib/git_fit/cli.rb CHANGED
@@ -61,6 +61,9 @@ module GitFit
61
61
  say_status :error, "Parse failed: #{e.message}", :red
62
62
  end
63
63
 
64
+ desc "install SUBCOMMAND", "Install project assets (actions)"
65
+ subcommand "install", InstallCLI
66
+
64
67
  desc "init", "Generate config.yml"
65
68
  option :output, type: :string, desc: "Output path", aliases: "-o"
66
69
  def init
@@ -20,7 +20,11 @@ module GitFit
20
20
 
21
21
  def run_migrations
22
22
  Sequel.extension :migration
23
- Sequel::Migrator.run(@db, File.expand_path("../../../db/migrations", __dir__))
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
@@ -1,3 +1,3 @@
1
1
  module GitFit
2
- VERSION = "0.4.6"
2
+ VERSION = "0.5.0"
3
3
  end
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.6
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lax
@@ -135,11 +135,13 @@ 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
138
139
  - lib/git_fit/config.rb
139
140
  - lib/git_fit/config_template.rb
140
141
  - lib/git_fit/db/activity.rb
141
142
  - lib/git_fit/db/cli.rb
142
143
  - lib/git_fit/db/connection.rb
144
+ - lib/git_fit/db/rebuild.rb
143
145
  - lib/git_fit/export.rb
144
146
  - lib/git_fit/export/defaults.rb
145
147
  - lib/git_fit/export/json.rb
@@ -151,6 +153,7 @@ files:
151
153
  - lib/git_fit/import.rb
152
154
  - lib/git_fit/import/apple_health.rb
153
155
  - lib/git_fit/import/local_file.rb
156
+ - lib/git_fit/install/actions.rb
154
157
  - lib/git_fit/parser.rb
155
158
  - lib/git_fit/parser/base.rb
156
159
  - lib/git_fit/parser/fit.rb