Skip to content

Commit a24c645

Browse files
dblockCopilot
andcommitted
Upgrade rack-server-pages and version website assets
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 5bfca35 commit a24c645

10 files changed

Lines changed: 76 additions & 21 deletions

File tree

‎.rubocop_todo.yml‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# This configuration was generated by
22
# `rubocop --auto-gen-config`
3-
# on 2026-10-25 15:08:27 UTC using RuboCop version 1.92.1.
3+
# on 2026-10-25 19:37:01 UTC using RuboCop version 1.92.1.
44
# The point is for the user to remove these configuration records
55
# one by one as the offenses are removed from the code base.
66
# Note that changes in the inspected code, or installation of new
@@ -72,7 +72,7 @@ RSpec/ContextWording:
7272
- 'spec/models/team_spec.rb'
7373
- 'spec/support/api/endpoints/it_behaves_like_a_cursor_api.rb'
7474

75-
# Offense count: 13
75+
# Offense count: 14
7676
# Configuration parameters: CountAsOne.
7777
RSpec/ExampleLength:
7878
Max: 11
@@ -94,7 +94,7 @@ RSpec/LetSetup:
9494
RSpec/MessageSpies:
9595
EnforcedStyle: receive
9696

97-
# Offense count: 29
97+
# Offense count: 34
9898
RSpec/MultipleExpectations:
9999
Max: 6
100100

@@ -128,7 +128,7 @@ RSpec/SpecFilePathFormat:
128128
- 'spec/commands/whoami_spec.rb'
129129
- 'spec/version_spec.rb'
130130

131-
# Offense count: 11
131+
# Offense count: 13
132132
# This cop supports safe autocorrection (--autocorrect).
133133
# Configuration parameters: AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, AllowRBSInlineAnnotation, AllowCopDirectives, AllowedPatterns, SplitStrings.
134134
# URISchemes: http, https

‎CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
### Changelog
22

3+
* 2026/10/25: [#88](https://github.com/slack-ruby/slack-shellbot/pull/88): Upgrade rack-server-pages to 0.3.0 and version website assets by content, with revalidation for HTML and generated CSS to prevent stale browser assets after deployments - [@dblock](https://github.com/dblock), [@Copilot](https://github.com/apps/copilot-swe-agent).
34
* 2026/10/25: [#87](https://github.com/slack-ruby/slack-shellbot/pull/87): Redesign the Shell website with shared responsive layouts, charcoal ribbon artwork, a black-bordered animated demo, an "Is this a joke?" FAQ linked from the homepage, and recommendations with original logos for Slava, Strada, Gamebot, and S'Up above Open source while preserving Slack installation and status flows - [@dblock](https://github.com/dblock), [@Copilot](https://github.com/apps/copilot-swe-agent).
45
* 2026/10/24: [#86](https://github.com/slack-ruby/slack-shellbot/pull/86): Upgrade slack-ruby-bot-server to 2.2.1 to fix Grape error handling for unsupported requests and failing health checks - [@dblock](https://github.com/dblock), [@Copilot](https://github.com/apps/copilot-swe-agent).
56
* 2026/10/23: Upgrade to Ruby 4.0.9 and refresh dependencies and CI browser tooling - [@dblock](https://github.com/dblock), [@Copilot](https://github.com/apps/copilot-swe-agent).

‎Gemfile‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ gem 'mongoid-scroll'
99
gem 'newrelic_rpm'
1010
gem 'puma'
1111
gem 'rack-robotz'
12-
gem 'rack-server-pages'
12+
gem 'rack-server-pages', '~> 0.3.0'
1313
gem 'slack-ruby-bot-server-events'
1414
gem 'slack-ruby-bot-server-mailchimp'
1515

‎Gemfile.lock‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ GEM
205205
rack-rewrite (1.5.1)
206206
rack-robotz (0.0.4)
207207
rack
208-
rack-server-pages (0.2.1)
208+
rack-server-pages (0.3.0)
209209
rack
210210
rack-test (2.3.0)
211211
rack (>= 1.3)
@@ -345,7 +345,7 @@ DEPENDENCIES
345345
newrelic_rpm
346346
puma
347347
rack-robotz
348-
rack-server-pages
348+
rack-server-pages (~> 0.3.0)
349349
rack-test
350350
rackup
351351
rake

‎README.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ The website follows the shared Slava, Strata, and Strada layout with a terminal-
1919

2020
The bot recommendations use local copies of the original logos: `bot-slava.png` and `bot-strada.png` from their respective projects' `public/img/icon.png`, `bot-gamebot.png` from slack-gamebot2's `public/img/game.png`, and `bot-sup.png` from slack-sup2's `public/img/icon.png`. Preserve their original colors rather than applying the Shell website's monochrome styling.
2121

22+
## Website assets and caching
23+
24+
The website uses `rack-server-pages` 0.3.0's `asset_path` helper for content-versioned CSS, JavaScript, images, and favicon URLs. Use the helper for new local asset references rather than fixed URLs or manual version strings. `public/styles.css.erb` is served at `/styles.css` and versions the background image reference too.
25+
26+
The shared head partial and generated stylesheet set `Cache-Control: private, no-cache`, requiring revalidation before reuse. This lets the stylesheet pick up a changed background image even when its own source is unchanged. Static files use the library's default content-based `ETag` and timestamp validation. Versioned URLs serve current files rather than archived versions; do not add `immutable` or long freshness lifetimes. Existing cached pages need to fetch fresh HTML before they can use the new URLs.
27+
2228
## Copyright and License
2329

2430
Copyright (c) 2016-2025, Daniel Doubrovkine, Artsy and [Contributors](CHANGELOG.md).

‎public/index.html.erb‎

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
<head>
44
<title>Shell: A Unix Shell in Every Slack Channel</title>
55
<%= partial 'public/partials/_head.html.erb' %>
6-
<script src="/scripts/register.js"></script>
7-
<script src="/scripts/status.js"></script>
6+
<script src="<%= h asset_path('/scripts/register.js') %>"></script>
7+
<script src="<%= h asset_path('/scripts/status.js') %>"></script>
88
<meta name="slack-app-id" content="A01F73M28BF">
99
<meta name="description" content="Add a rudimentary Unix shell to every Slack channel.">
1010
</head>
@@ -13,7 +13,7 @@
1313
<main id="main">
1414
<section class="hero page-container" aria-labelledby="hero-title">
1515
<div class="hero-intro">
16-
<img class="hero-logo" src="/img/icon.png" width="512" height="512" alt="Shell terminal logo">
16+
<img class="hero-logo" src="<%= h asset_path('/img/icon.png') %>" width="512" height="512" alt="Shell terminal logo">
1717
<div>
1818
<h1 id="hero-title">Shell for Slack</h1>
1919
<p class="hero-tagline">Whoa, a bash shell inside Slack!</p>
@@ -28,7 +28,7 @@
2828
</div>
2929
</div>
3030
<figure class="hero-preview">
31-
<img src="/img/shell.gif" width="691" height="419" alt="Shell running Unix commands inside a Slack channel">
31+
<img src="<%= h asset_path('/img/shell.gif') %>" width="691" height="419" alt="Shell running Unix commands inside a Slack channel">
3232
<figcaption>A little command line, right where your team works.</figcaption>
3333
</figure>
3434
</section>
@@ -47,25 +47,25 @@
4747
<p class="section-intro">A little more play, a little more connection.</p>
4848
<ul class="bot-cards">
4949
<li class="bot-card">
50-
<img class="bot-logo" src="/img/bot-slava.png" width="72" height="72" alt="" loading="lazy">
50+
<img class="bot-logo" src="<%= h asset_path('/img/bot-slava.png') %>" width="72" height="72" alt="" loading="lazy">
5151
<h3>Strava in Slack</h3>
5252
<p>Share Strava activities, celebrate every finish, and keep your Slack team moving.</p>
5353
<a class="secondary-button" href="https://slava.playplay.io/">Meet Slava</a>
5454
</li>
5555
<li class="bot-card">
56-
<img class="bot-logo" src="/img/bot-strada.png" width="72" height="72" alt="" loading="lazy">
56+
<img class="bot-logo" src="<%= h asset_path('/img/bot-strada.png') %>" width="72" height="72" alt="" loading="lazy">
5757
<h3>Strava in Discord</h3>
5858
<p>Bring your running or cycling club together with Strava activities in Discord.</p>
5959
<a class="secondary-button" href="https://strada.playplay.io/">Meet Strada</a>
6060
</li>
6161
<li class="bot-card">
62-
<img class="bot-logo" src="/img/bot-sup.png" width="72" height="72" alt="" loading="lazy">
62+
<img class="bot-logo" src="<%= h asset_path('/img/bot-sup.png') %>" width="72" height="72" alt="" loading="lazy">
6363
<h3>S'Up for Slack</h3>
6464
<p>Meet someone new over coffee with fresh groups of three from your Slack channel each week.</p>
6565
<a class="secondary-button" href="https://sup2.playplay.io/">Meet S'Up</a>
6666
</li>
6767
<li class="bot-card">
68-
<img class="bot-logo" src="/img/bot-gamebot.png" width="72" height="72" alt="" loading="lazy">
68+
<img class="bot-logo" src="<%= h asset_path('/img/bot-gamebot.png') %>" width="72" height="72" alt="" loading="lazy">
6969
<h3>Leaderboards in Slack</h3>
7070
<p>Challenge teammates, report scores, and climb your Slack channel's leaderboard.</p>
7171
<a class="secondary-button" href="https://gamebot2.playplay.io/">Meet Gamebot</a>

‎public/partials/_head.html.erb‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
<% headers['Cache-Control'] = 'private, no-cache' %>
12
<meta charset="utf-8">
23
<meta name="viewport" content="width=device-width, initial-scale=1">
3-
<link rel="icon" href="/favicon.ico?v=charcoal" type="image/x-icon">
4-
<script src="/scripts/jquery-1.7.1.min.js"></script>
5-
<script src="/scripts/url.min.js"></script>
6-
<script src="/scripts/shell.js"></script>
7-
<link rel="stylesheet" type="text/css" href="/styles.css">
4+
<link rel="icon" href="<%= h asset_path('/favicon.ico') %>" type="image/x-icon">
5+
<script src="<%= h asset_path('/scripts/jquery-1.7.1.min.js') %>"></script>
6+
<script src="<%= h asset_path('/scripts/url.min.js') %>"></script>
7+
<script src="<%= h asset_path('/scripts/shell.js') %>"></script>
8+
<link rel="stylesheet" type="text/css" href="<%= h asset_path('/styles.css') %>">
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
<% headers['Cache-Control'] = 'private, no-cache' %>
12
html {
23
scrollbar-gutter: stable;
34
}
@@ -25,7 +26,7 @@ body {
2526
z-index: -1;
2627
content: "";
2728
pointer-events: none;
28-
background: linear-gradient(#ffffff66, #ffffff66), url("/img/screenshot-background.svg") center top / 100% 100% no-repeat;
29+
background: linear-gradient(#ffffff66, #ffffff66), url("<%= asset_path('/img/screenshot-background.svg') %>") center top / 100% 100% no-repeat;
2930
}
3031

3132
.site-page > main { flex: 1; }

‎spec/integration/index_spec.rb‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@
2323
expect(page).to have_css('#is-this-a-joke p', text: 'Yes. Also, it works!')
2424
end
2525

26+
it 'loads the generated stylesheet and versioned artwork after a reload' do
27+
page.refresh
28+
background = page.evaluate_script("getComputedStyle(document.body, '::before').backgroundImage")
29+
expect(background).to match(%r{/img/screenshot-background\.svg\?v=[a-f0-9]{64}})
30+
expect(page).to have_css('.hero-preview img') { |image| image.evaluate_script('this.complete && this.naturalWidth > 0') }
31+
end
32+
2633
it 'links to the bot recommendations after How to use in navigation' do
2734
expect(page).to have_css('.nav-links a[href="/#how-to-use"] + a[href="/#more-bots"]', text: /More bots/i)
2835
within('nav') { click_link 'More bots' }

‎spec/slack_ruby_bot_server/api/middleware_spec.rb‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,45 @@
1212
expect(JSON.parse(last_response.body)).to include('type' => 'other_error', 'message' => '405 Not Allowed')
1313
end
1414

15+
describe 'website asset caching' do
16+
['/', '/help.html', '/privacy.html'].each do |path|
17+
it "serves #{path} with versioned local assets and a revalidating cache policy" do
18+
get path
19+
20+
assets = Capybara.string(last_response.body).all('script[src^="/"], link[href^="/"], img[src^="/"]', visible: :all)
21+
expect(assets).not_to be_empty
22+
expect(assets.map { |asset| asset[:src] || asset[:href] }).to all(match(/\?v=[a-f0-9]{64}\z/))
23+
expect(last_response.headers['Cache-Control']).to eq 'private, no-cache'
24+
end
25+
end
26+
27+
it 'renders a revalidating stylesheet with a versioned background image' do
28+
get '/styles.css'
29+
30+
expect(last_response.content_type).to eq 'text/css; charset=utf-8'
31+
expect(last_response.headers['Cache-Control']).to eq 'private, no-cache'
32+
expect(last_response.body).to include("/img/screenshot-background.svg?v=#{Digest::SHA256.file('public/img/screenshot-background.svg').hexdigest}")
33+
end
34+
35+
it 'revalidates static assets using their content ETag' do
36+
get '/scripts/shell.js'
37+
etag = last_response.headers['ETag']
38+
expect(etag).to eq %("#{Digest::SHA256.file('public/scripts/shell.js').hexdigest}")
39+
40+
get '/scripts/shell.js', {}, 'HTTP_IF_NONE_MATCH' => etag
41+
expect(last_response.status).to eq 304
42+
expect(last_response.body).to be_empty
43+
end
44+
45+
it 'returns the asset for an old ETag even when its timestamp matches' do
46+
get '/scripts/shell.js', {}, 'HTTP_IF_NONE_MATCH' => '"old-content"',
47+
'HTTP_IF_MODIFIED_SINCE' => File.mtime('public/scripts/shell.js').httpdate
48+
49+
expect(last_response.status).to eq 200
50+
expect(last_response.body).to eq File.read('public/scripts/shell.js')
51+
end
52+
end
53+
1554
context 'when a health check fails' do
1655
let(:team) { Fabricate(:team) }
1756

0 commit comments

Comments
 (0)