Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Production deploy: Fetch and display sign database from S3 location #1528

Merged
merged 20 commits into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
6a0dddd
Add dependency on aws-sdk-s3 to download the dictionary file from S3
joshmcarthur Jan 10, 2024
d203ee8
Add code to update dictionary from S3 from nzsl-share
joshmcarthur Jan 10, 2024
8a6a115
Copy env-example during CI run to load default config
joshmcarthur Jan 10, 2024
139a9c2
Use correct model name for Dictionary
joshmcarthur Jan 10, 2024
a8d4a45
Update the sign database each time Rails starts
joshmcarthur Jan 10, 2024
4e87a2b
Provide AWS_REGION for CI configuration
joshmcarthur Jan 10, 2024
2d629a3
Add aws-sdk-s3 to allow presigned URLs to be generated
joshmcarthur Jan 11, 2024
bae5dac
Import Signbank::AssetURL model to handle presigning sign assets wher…
joshmcarthur Jan 11, 2024
2bc14f8
Use Signbank::AssetURL for examples and sign video
joshmcarthur Jan 11, 2024
2a5910a
NZSL-164: Show the database version in the footer
joshmcarthur Jan 11, 2024
bfb223d
Update footer to add alt text to license badge
joshmcarthur Jan 14, 2024
acfcdb1
Merge pull request #1523 from ODNZSL/task/nzsl-159-update-nzsl-dictio…
joshmcarthur Jan 15, 2024
64b8f31
Apply patch to handle empty string column values for sign media
joshmcarthur Jan 15, 2024
b5f9c74
Merge pull request #1525 from ODNZSL/task/nzsl-164-show-database-version
joshmcarthur Jan 15, 2024
e3e1422
Correct string formatting violations with rubocop
joshmcarthur Jan 15, 2024
228d5f1
Merge pull request #1524 from ODNZSL/task/nzsl-167-presign-sign-asset…
joshmcarthur Jan 15, 2024
72a194e
Add some conditionals to handle prerelease signs missing videos
joshmcarthur Jan 15, 2024
a0c33ff
Merge pull request #1526 from ODNZSL/fix/handle-missing-video
joshmcarthur Jan 15, 2024
140685a
Revert "NZSL-167: Presign asset requests"
joshmcarthur Jan 16, 2024
186374d
Merge pull request #1527 from ODNZSL/revert-1524-task/nzsl-167-presig…
joshmcarthur Jan 17, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,16 +94,19 @@ jobs:
env:
DATABASE_URL: postgres://postgres:postgres@localhost:5432/nzsl_test
DEVISE_SECRET_KEY: anything
AWS_REGION: ap-southeast-2
RAILS_ENV: test
run: |
cp env-example .env
bundle exec rails db:prepare

- name: Run rspec
env:
DATABASE_URL: postgres://postgres:postgres@localhost:5432/nzsl_test
DEVISE_SECRET_KEY: anything
NZSL_ONLINE_SECRET_KEY_BASE: anything
APP_DOMAIN_NAME: localhost:3000
APP_PROTOCOL: http
AWS_REGION: ap-southeast-2
S3_BUCKET_URL: http://s3-ap-southeast-2.amazonaws.com/dummy-fake/
run: bundle exec rspec spec
run: |
cp env-example .env
bundle exec rspec spec
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ gem 'pg', '~>1.2'
# Use SQLite to access signs from a Signbank dictionary export
gem 'sqlite3'

gem 'aws-sdk-s3'
gem 'bootsnap', '>= 1.1.0', require: false
gem 'haml'
gem 'jquery-rails'
Expand Down
18 changes: 18 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,22 @@ GEM
ast (2.4.2)
autoprefixer-rails (10.3.3.0)
execjs (~> 2)
aws-eventstream (1.3.0)
aws-partitions (1.878.0)
aws-sdk-core (3.190.2)
aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.651.0)
aws-sigv4 (~> 1.8)
jmespath (~> 1, >= 1.6.1)
aws-sdk-kms (1.76.0)
aws-sdk-core (~> 3, >= 3.188.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.142.0)
aws-sdk-core (~> 3, >= 3.189.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.8)
aws-sigv4 (1.8.0)
aws-eventstream (~> 1, >= 1.0.2)
babel-source (5.8.35)
babel-transpiler (0.7.0)
babel-source (>= 4.0, < 6)
Expand Down Expand Up @@ -175,6 +191,7 @@ GEM
multi_xml (>= 0.5.2)
i18n (1.14.1)
concurrent-ruby (~> 1.0)
jmespath (1.6.2)
jquery-rails (4.4.0)
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
Expand Down Expand Up @@ -420,6 +437,7 @@ PLATFORMS

DEPENDENCIES
autoprefixer-rails
aws-sdk-s3
bootsnap (>= 1.1.0)
brakeman
bundle-audit
Expand Down
4 changes: 4 additions & 0 deletions app/models/signbank/record.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ class Record < ApplicationRecord
# to create test records
after_initialize { readonly! unless Rails.env.test? }

def self.database_version
connection.execute('PRAGMA user_version').first['user_version']
end

##
# Useful for testing - related Signbank records don't have primary keys,
# so in this case we consider them equal if they are the same type and
Expand Down
27 changes: 17 additions & 10 deletions app/views/shared/_footer.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,24 @@
</div>
<div class="copyright">
<div class="small-10 small-centered medium-8 large-6">
<% if @footer && @footer.first_part %>
<%= link_to((image_tag "https://i.creativecommons.org/l/by-nc-sa/3.0/88x31.png"), "https://creativecommons.org/licenses/by-nc-sa/3.0", target:"_blank" ) %>
<div class="footer-link">
NZSL Dictionary by
<%= link_to "Deaf Studies Research Unit, Victoria University of Wellington", "https://www.victoria.ac.nz/lals/research/projects/dsru.aspx", target: "_blank" %>
is licensed under a
<%= link_to "Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.","https://creativecommons.org/licenses/by-nc-sa/4.0/", target: "_blank" %>
<br/>
<%= link_to 'View in NZSL', @footer.path %>
</div>
<%= link_to "https://creativecommons.org/licenses/by-nc-sa/3.0", target:"_blank" do %>
<%= image_tag "https://i.creativecommons.org/l/by-nc-sa/3.0/88x31.png", alt: "Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License badge" %>
<% end %>

<div class="footer-link">
NZSL Dictionary by
<%= link_to "Deaf Studies Research Unit, Victoria University of Wellington", "https://www.victoria.ac.nz/lals/research/projects/dsru.aspx", target: "_blank" %>
is licensed under a
<%= link_to "Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.","https://creativecommons.org/licenses/by-nc-sa/4.0/", target: "_blank" %>

<% if (version = Signbank::Record.database_version) %>
<br />
Database version <%= version %>.
<% end %>

<br/>
<%= link_to 'View in NZSL', @footer.path if @footer && @footer.first_part %>
</div>
</div>
</div>
</footer>
2 changes: 1 addition & 1 deletion app/views/signs/_sign_of_the_day.html.haml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
- if sign.present?
- if sign.present? && sign.video
.sign-of-the-day.text-center
.column-block
%h3= t('signs.sign_of_the_day')
Expand Down
7 changes: 4 additions & 3 deletions app/views/signs/search.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
- @signs.each do |sign|
.search-results__card
- @query[:s] ? query_text = @query[:s].join(" ") : query_text = " "
.video-container.video--placeholder
%i.fi-play.play-button
= videojs_rails sources: { mp4: sign.video }, class: "normal", controlsList:"nodownload", controls: false, preload: "metadata", loop: true
- if sign.video
.video-container.video--placeholder
%i.fi-play.play-button
= videojs_rails sources: { mp4: sign.video }, class: "normal", controlsList:"nodownload", controls: false, preload: "metadata", loop: true
-# %span.search-results--placeholder
.clickable_link.small-12.small-centered
%a.div_link{:href => "#{sign_url(sign.id)}"}
Expand Down
12 changes: 7 additions & 5 deletions app/views/signs/show.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,12 @@
%i.fi-plus
%span.buttons-action.add-button-text Add to Vocab Sheet
.videos.small-12.medium-5
.video-container
%i.fi-play.play-button
= videojs_rails sources: { mp4: @sign.video }, class: "main_video video", controlsList:"nodownload", controls: true, preload: "metadata", loop: true
= play_video_button('signs.show.in_slow_motion', nil, class: 'float-left slow')
= play_video_button('signs.show.at_normal_speed', nil, class: 'float-left normal')
- if @sign.video
.video-container
%i.fi-play.play-button
= videojs_rails sources: { mp4: @sign.video }, class: "main_video video", controlsList:"nodownload", controls: true, preload: "metadata", loop: true
= play_video_button('signs.show.in_slow_motion', nil, class: 'float-left slow')
= play_video_button('signs.show.at_normal_speed', nil, class: 'float-left normal')
.glosses-container.glosses.small-12.float-left
%h2.main_gloss= @sign.gloss_main
%h2.secondary_gloss= @sign.gloss_secondary
Expand All @@ -44,6 +45,7 @@
.examples-container.clearfix.videos.small-12.small-centered.medium-5.medium-uncentered
%h3= t('signs.show.usage_examples')
- @sign.examples.each do |example|
- next unless example.video
.typography.videos
.video-container
%i.fi-play.play-button
Expand Down
13 changes: 5 additions & 8 deletions config/initializers/sign_database.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
# Update the dictionary file if it is older than 1 month
# We update this file in both dictionary modes because our tests
# expect the database to test across both modes
path = Rails.root.join('db', 'dictionary.sqlite3')
Rails.application.load_tasks
deployed = !Rails.env.development? && !Rails.env.test?

Rake::Task['dictionary:update'].execute if deployed || (!path.exist? || path.mtime <= 1.month.ago)
Rails.application.reloader.to_prepare do
# Update the dictionary file on boot
Rails.application.load_tasks
Rake::Task['dictionary:update'].execute
end
5 changes: 4 additions & 1 deletion env-example
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
S3_BUCKET_URL: "example.s3.url/"
NZSL_ONLINE_SECRET_KEY_BASE: 62da7bed624d0cbbe3d186166fdd88db5bb3989075a2154cebe3e5ee20a4f2a2d540865309958346b7b43799d461be2b37c6e27d1fd6ca03b1f59622c5ccc402
APP_DOMAIN_NAME: "localhost:3000"
APP_PROTOCOL: "http"
APP_PROTOCOL: "http"

# The latest public release
DICTIONARY_DATABASE_S3_LOCATION="s3://nzsl-signbank-media-production/dictionary-exports/nzsl.db"
54 changes: 36 additions & 18 deletions lib/tasks/dictionary.rake
Original file line number Diff line number Diff line change
@@ -1,29 +1,47 @@
namespace :dictionary do
namespace :dictionary do # rubocop:disable Metrics/BlockLength
desc 'Updates the NZSL dictionary packaged with the application to the latest release from Signbank'
task :update do # rubocop:disable Rails/RakeEnvironment - we need to place this file before the app can start
repo = 'odnzsl/nzsl-dictionary-scripts'
filename = 'nzsl.db'
content_type = 'application/vnd.sqlite3'
release_uri = URI::HTTPS.build(host: 'api.github.com', path: "/repos/#{repo}/releases/latest")
release = JSON.parse(release_uri.open.read)
database_asset = release['assets'].find do |asset|
asset['name'] == filename && asset['content_type'] == content_type
end

database_url = database_asset.fetch('browser_download_url')
database_s3_location = URI.parse(ENV.fetch('DICTIONARY_DATABASE_S3_LOCATION') || '')
raise 'DICTIONARY_DATABASE_S3_LOCATION must be an S3 URI' unless database_s3_location.scheme == 's3'

File.open('db/new-dictionary.sqlite3', 'wb') do |f|
f.write URI.parse(database_url).open.read
rescue OpenURI::HTTPError
sleep 5 # Wait a few seconds before retrying
retry
end
download_s3_uri(database_s3_location, 'db/new-dictionary.sqlite3')

database = SQLite3::Database.open('db/new-dictionary.sqlite3')
raise 'Database does not pass integrity check' unless database.integrity_check == [['ok']]

version = database.get_int_pragma('user_version')

FileUtils.mv('db/new-dictionary.sqlite3', 'db/dictionary.sqlite3')

puts "Updated db/dictionary.sqlite3 to #{release['name']}"
puts "Updated db/dictionary.sqlite3 to #{version}"
end

def s3_client
@s3_client ||= Aws::S3::Client.new({
region: ENV.fetch('DICTIONARY_AWS_REGION', ENV.fetch('AWS_REGION', nil)),
access_key_id: ENV.fetch('DICTIONARY_AWS_ACCESS_KEY_ID', nil),
secret_access_key: ENV.fetch('DICTIONARY_AWS_SECRET_ACCESS_KEY', nil)
}.compact)
end

def download_s3_uri(s3_uri, target)
bucket = s3_uri.host
key = s3_uri.path[1..]

begin
s3_client.get_object({ bucket:, key: }, target:)
rescue Aws::Errors::MissingCredentialsError,
Aws::Sigv4::Errors::MissingCredentialsError,
Aws::S3::Errors::ServiceError

# Fallback to public-URL download over HTTP if credentials are not provided or invalid.
# TODO use aws-sdk to leverage aws-client optimizations once unsigned requests are supported:
# https://github.com/aws/aws-sdk-ruby/issues/1149
public_url = URI.parse(Aws::S3::Bucket.new(bucket, credentials: 0).object(key).public_url)
Net::HTTP.start(public_url.host, public_url.port, use_ssl: true) do |http|
response = http.get(public_url.request_uri).tap(&:value)
File.binwrite(target, response.body)
end
end
end
end
14 changes: 14 additions & 0 deletions spec/models/signbank/record_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
require 'rails_helper'

RSpec.describe Signbank::Record do
describe '.database_version' do
it 'returns the user version of the database' do
version = 42
allow(Signbank::Record.connection).to receive(:execute)
.with('PRAGMA user_version')
.and_return([{ 'user_version' => version }])

expect(Signbank::Record.database_version).to eq(version)
end
end
end
15 changes: 15 additions & 0 deletions spec/views/shared/_footer.html.erb_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
require 'rails_helper'

RSpec.describe 'shared/_footer.html.erb' do
it 'renders the database version when available' do
allow(Signbank::Record).to receive(:database_version).and_return(123_456)
render partial: 'shared/footer', locals: { last: false }
expect(rendered).to include 'Database version 123456'
end

it 'does not render the database version if it is unavailable' do
allow(Signbank::Record).to receive(:database_version).and_return(nil)
render partial: 'shared/footer', locals: { last: false }
expect(rendered).not_to include 'Database version 123456'
end
end
Loading