lsst-git-lfs-s3 0.3.0 → 0.3.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/README.md +3 -20
- data/git-lfs-s3.gemspec +3 -3
- data/lib/git-lfs-s3/application.rb +72 -82
- data/lib/git-lfs-s3/version.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c281e6b4092827cee2ff392f15e5edfcd87e5a54
|
4
|
+
data.tar.gz: b17910a8176a51e1be82853e7cf68dde2f145abc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ea4536a92be8d1a73173de01f92ac1efc0d95c08b6f035a8544902fca1895d76b1342bcd575f82cd8f49df0b5a079e852810d1e1aab0ca93c0f8c01dae268469
|
7
|
+
data.tar.gz: a20908659964e73b8452aba86ff4afe0a43181c3f9547dc6e587594cc40d6a4ffb967c27c87bbd1a0f7b7e0023a3315ab3aabc8642976fe6d94fab25d9dc8716
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Git LFS S3
|
1
|
+
# LSST's Git LFS S3
|
2
2
|
|
3
3
|
A [Git LFS](https://git-lfs.github.com/) server that stores your large Git files on S3.
|
4
4
|
|
@@ -9,7 +9,7 @@ It works by generating a presigned URL that the Git LFS client can use to upload
|
|
9
9
|
Git LFS S3 is available on RubyGems.
|
10
10
|
|
11
11
|
``` bash
|
12
|
-
gem install git-lfs-s3
|
12
|
+
gem install lsst-git-lfs-s3
|
13
13
|
```
|
14
14
|
|
15
15
|
Or add it to your Gemfile if you wish to bundle it as a part of another application.
|
@@ -29,9 +29,6 @@ All configuration is done via environment variables. All of these configuration
|
|
29
29
|
* `AWS_SECRET_ACCESS_KEY` - your AWS secret key.
|
30
30
|
* `S3_BUCKET` - the bucket you wish to use for LFS storage. While not required, I recommend using a dedicated bucket for this.
|
31
31
|
* `LFS_SERVER_URL` - the URL where this server can be reached; needed to fetch download URLs.
|
32
|
-
* `LFS_PUBLIC_SERVER` - (Optional) Support anonymous users for safe operations such as git-clone and git-pull.
|
33
|
-
* `LFS_CEPH_S3` - (Optional) support [Ceph S3](http://ceph.com/) (Hammer version, through radosgw).
|
34
|
-
* `LFS_CEPH_ENDPOINT` - (Optional) the Ceph S3 endpoint URL. Required when using `LFS_CEPH_S3`.
|
35
32
|
|
36
33
|
You can (and should) also set authentication information. When you push for the first time from git, you will be prompted to enter a username and password when authentication is enabled. You can configure these with environment variables as well.
|
37
34
|
|
@@ -68,7 +65,6 @@ If you are new to Git LFS, make sure you read the [Getting Started](https://git-
|
|
68
65
|
``` git
|
69
66
|
[lfs]
|
70
67
|
url = "http://yourserver.com"
|
71
|
-
batch = false
|
72
68
|
```
|
73
69
|
|
74
70
|
Once that is done, you can tell Git LFS to track files with `git lfs track "*.psd"`, for example.
|
@@ -87,19 +83,6 @@ However, because this is a Sinatra application, it can also be mounted within ot
|
|
87
83
|
mount GitLfsS3::Application => '/lfs'
|
88
84
|
```
|
89
85
|
|
90
|
-
|
91
|
-
|
92
|
-
Allow anonymous clones and pulls of the git-lfs git repository.
|
93
|
-
|
94
|
-
## Ceph S3 Server
|
95
|
-
|
96
|
-
Through the `LFS_CEPH_S3` and `LFS_CEPH_ENDPOINT` environment variables use a Ceph S3 storage service (Hammer version, through radosgw) instead of AWS S3.
|
97
|
-
|
98
|
-
## More Complex Server Example
|
99
|
-
|
100
|
-
* https://github.com/lsst-sqre/git-lfs-s3-server
|
101
|
-
|
102
86
|
## TODO
|
103
87
|
|
104
|
-
* Cloudfront support
|
105
|
-
* Batch API support.
|
88
|
+
* Cloudfront support
|
data/git-lfs-s3.gemspec
CHANGED
@@ -8,9 +8,9 @@ Gem::Specification.new do |gem|
|
|
8
8
|
gem.version = GitLfsS3::VERSION
|
9
9
|
gem.authors = ["Ryan LeFevre", "J. Matt Peterson"]
|
10
10
|
gem.email = ["meltingice8917@gmail.com", "jmatt@lsst.org"]
|
11
|
-
gem.description = %q{
|
12
|
-
gem.summary = %q{
|
13
|
-
gem.homepage = "https://github.com/
|
11
|
+
gem.description = %q{LSST's Git LFS server that uses S3 for the storage backend.}
|
12
|
+
gem.summary = %q{LSST's Git LFS server that uses S3 for the storage backend by providing presigned S3 URLs.}
|
13
|
+
gem.homepage = "https://github.com/lsst-sqre/git-lfs-s3"
|
14
14
|
gem.license = 'MIT'
|
15
15
|
|
16
16
|
gem.files = `git ls-files`.split($/)
|
@@ -49,142 +49,142 @@ module GitLfsS3
|
|
49
49
|
"Git LFS S3 is online."
|
50
50
|
end
|
51
51
|
|
52
|
-
def
|
52
|
+
def valid_obj?(obj)
|
53
|
+
# Validate that size >= 0 and oid is a SHA256 hash.
|
53
54
|
begin
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
end
|
58
|
-
begin
|
59
|
-
if valid
|
60
|
-
oid = object[:oid].hex
|
61
|
-
valid = oid.size == 32 && object[:oid].size == 64
|
55
|
+
if obj[:size] >= 0
|
56
|
+
oid = obj[:oid]
|
57
|
+
valid = (oid.hex.size <= 32) and (oid.size == 64) and (oid =~ /^[0-9a-f]+$/)
|
62
58
|
end
|
63
|
-
rescue
|
64
|
-
valid = false
|
65
59
|
end
|
66
|
-
valid
|
67
60
|
end
|
68
61
|
|
69
|
-
def
|
70
|
-
|
71
|
-
|
62
|
+
def expire_at()
|
63
|
+
DateTime.now.next_day.to_time.utc.iso8601
|
64
|
+
end
|
65
|
+
|
66
|
+
def obj_download(authenticated, obj, obj_json)
|
67
|
+
# Format a single download object.
|
68
|
+
oid = obj_json[:oid]
|
69
|
+
size = obj_json[:size]
|
72
70
|
{
|
73
|
-
'oid'
|
74
|
-
'size'
|
71
|
+
'oid' => oid,
|
72
|
+
'size' => size,
|
75
73
|
'authenticated' => authenticated,
|
76
|
-
'actions'
|
77
|
-
'download'
|
78
|
-
'href'
|
79
|
-
|
74
|
+
'actions' => {
|
75
|
+
'download' => {
|
76
|
+
'href' => obj.presigned_url(:get,
|
77
|
+
:expires_in => 86400),
|
78
|
+
},
|
80
79
|
},
|
81
|
-
'expires_at'
|
80
|
+
'expires_at' => expire_at,
|
82
81
|
}
|
83
82
|
end
|
84
83
|
|
85
|
-
def
|
84
|
+
def obj_upload(authenticated, obj, obj_json)
|
86
85
|
# Format a single upload object.
|
87
|
-
oid =
|
88
|
-
size =
|
86
|
+
oid = obj_json[:oid]
|
87
|
+
size = obj_json[:size]
|
89
88
|
{
|
90
|
-
'oid'
|
91
|
-
'size'
|
89
|
+
'oid' => oid,
|
90
|
+
'size' => size,
|
92
91
|
'authenticated' => authenticated,
|
93
|
-
'actions'
|
94
|
-
'upload'
|
95
|
-
'href'
|
96
|
-
|
92
|
+
'actions' => {
|
93
|
+
'upload' => {
|
94
|
+
'href' => obj.presigned_url(:put,
|
95
|
+
acl: 'public-read',
|
96
|
+
:expires_in => 86400),
|
97
|
+
},
|
98
|
+
'expires_at' => expire_at,
|
97
99
|
},
|
98
|
-
'expires_at' => DateTime.now.next_day.to_time.utc.iso8601
|
99
100
|
}
|
100
101
|
end
|
101
102
|
|
102
|
-
def
|
103
|
+
def obj_error(error, message, obj_json)
|
104
|
+
# Format a single error object.
|
103
105
|
{
|
104
|
-
'oid'
|
105
|
-
'size'
|
106
|
-
'error'
|
107
|
-
'code'
|
108
|
-
'message' => message
|
109
|
-
}
|
106
|
+
'oid' => obj_json[:oid],
|
107
|
+
'size' => obj_json[:size],
|
108
|
+
'error' => {
|
109
|
+
'code' => error,
|
110
|
+
'message' => message,
|
111
|
+
},
|
110
112
|
}
|
111
113
|
end
|
112
114
|
|
113
115
|
def download(authenticated, params)
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
116
|
+
# Handle git-lfs batch downloads.
|
117
|
+
objects = []
|
118
|
+
params[:objects].each do |obj_json|
|
119
|
+
obj_json = indifferent_params(obj_json)
|
120
|
+
obj = object_data(obj_json[:oid])
|
121
|
+
if valid_obj?(obj_json)
|
122
|
+
if obj.exists?
|
123
|
+
objects.push(obj_download(authenticated, obj, obj_json))
|
121
124
|
else
|
122
|
-
objects.push
|
125
|
+
objects.push(obj_error(404, 'Object does not exist', obj_json))
|
123
126
|
end
|
124
127
|
else
|
125
|
-
objects.push
|
128
|
+
objects.push(obj_error(422, 'Validation error', obj_json))
|
126
129
|
end
|
127
130
|
end
|
128
131
|
objects
|
129
132
|
end
|
130
133
|
|
131
134
|
def upload(authenticated, params)
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
135
|
+
# Handle git-lfs batch uploads.
|
136
|
+
objects = []
|
137
|
+
params[:objects].each do |obj_json|
|
138
|
+
obj_json = indifferent_params(obj_json)
|
139
|
+
obj = object_data(obj_json[:oid])
|
140
|
+
if valid_obj?(obj_json)
|
141
|
+
if obj.exists?
|
142
|
+
objects.push(obj_download(authenticated, obj, obj_json))
|
139
143
|
else
|
140
|
-
objects.push
|
144
|
+
objects.push(obj_upload(authenticated, obj, obj_json))
|
141
145
|
end
|
142
146
|
else
|
143
|
-
objects.push
|
147
|
+
objects.push(obj_error(422, 'Validation error', obj_json))
|
144
148
|
end
|
145
149
|
end
|
146
150
|
objects
|
147
|
-
|
151
|
+
end
|
148
152
|
|
149
153
|
def lfs_resp(objects)
|
150
|
-
|
154
|
+
# Successful git-lfs batch response.
|
155
|
+
status(200)
|
151
156
|
resp = {
|
152
157
|
'transfer' => 'basic',
|
153
158
|
'objects' => objects
|
154
159
|
}
|
155
|
-
logger.debug resp
|
156
160
|
body MultiJson.dump(resp)
|
157
161
|
end
|
158
162
|
|
159
163
|
def error_resp(status_code, message)
|
160
|
-
|
164
|
+
# Error git-lfs batch response.
|
165
|
+
status(status_code)
|
161
166
|
resp = {
|
162
167
|
'message' => message,
|
163
168
|
'request_id' => SecureRandom::uuid
|
164
169
|
}
|
165
|
-
logger.debug resp
|
166
170
|
body MultiJson.dump(resp)
|
167
171
|
end
|
168
172
|
|
169
173
|
post '/objects/batch', provides: 'application/vnd.git-lfs+json' do
|
170
|
-
#
|
174
|
+
# git-lfs batch API
|
171
175
|
authenticated = authorized?
|
172
176
|
params = indifferent_params(JSON.parse(request.body.read))
|
173
|
-
|
177
|
+
logger.debug params
|
174
178
|
if params[:operation] == 'download'
|
175
|
-
|
179
|
+
lfs_resp(download(authenticated, params))
|
176
180
|
elsif params[:operation] == 'upload'
|
177
181
|
if authenticated
|
178
|
-
|
182
|
+
lfs_resp(upload(authenticated, params))
|
179
183
|
else
|
180
|
-
|
184
|
+
error_resp(401, 'Credentials needed')
|
181
185
|
end
|
182
|
-
end
|
183
|
-
|
184
|
-
if objects
|
185
|
-
lfs_resp(objects)
|
186
186
|
else
|
187
|
-
error_resp(
|
187
|
+
error_resp(422, 'Validation error')
|
188
188
|
end
|
189
189
|
end
|
190
190
|
|
@@ -235,16 +235,6 @@ module GitLfsS3
|
|
235
235
|
body MultiJson.dump(service.response)
|
236
236
|
end
|
237
237
|
|
238
|
-
post "/objects/batch", provides: 'application/vnd.git-lfs+json' do
|
239
|
-
logger.debug headers.inspect
|
240
|
-
service = UploadBatchService.service_for(request.body)
|
241
|
-
logger.debug service.response
|
242
|
-
|
243
|
-
status service.status
|
244
|
-
body MultiJson.dump(service.response)
|
245
|
-
end
|
246
|
-
|
247
|
-
|
248
238
|
post '/verify', provides: 'application/vnd.git-lfs+json' do
|
249
239
|
data = MultiJson.load(request.body.tap { |b| b.rewind }.read)
|
250
240
|
object = object_data(data['oid'])
|
data/lib/git-lfs-s3/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lsst-git-lfs-s3
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan LeFevre
|
@@ -67,7 +67,7 @@ dependencies:
|
|
67
67
|
- - "~>"
|
68
68
|
- !ruby/object:Gem::Version
|
69
69
|
version: '10'
|
70
|
-
description:
|
70
|
+
description: LSST's Git LFS server that uses S3 for the storage backend.
|
71
71
|
email:
|
72
72
|
- meltingice8917@gmail.com
|
73
73
|
- jmatt@lsst.org
|
@@ -91,7 +91,7 @@ files:
|
|
91
91
|
- lib/git-lfs-s3/services/upload/object_exists.rb
|
92
92
|
- lib/git-lfs-s3/services/upload/upload_required.rb
|
93
93
|
- lib/git-lfs-s3/version.rb
|
94
|
-
homepage: https://github.com/
|
94
|
+
homepage: https://github.com/lsst-sqre/git-lfs-s3
|
95
95
|
licenses:
|
96
96
|
- MIT
|
97
97
|
metadata: {}
|
@@ -114,6 +114,6 @@ rubyforge_project:
|
|
114
114
|
rubygems_version: 2.6.10
|
115
115
|
signing_key:
|
116
116
|
specification_version: 4
|
117
|
-
summary:
|
117
|
+
summary: LSST's Git LFS server that uses S3 for the storage backend by providing presigned
|
118
118
|
S3 URLs.
|
119
119
|
test_files: []
|