Skip to content

Commit 1f478bc

Browse files
committed
deploy: improve support for external HTTPS
The official `git-scm.com` website is currently built by Hugo with `--baseURL http://git-scm.com/`. This commit allows changing the URL scheme to `https`, in order to avoid unnecessary redirects between HTTPS and unencrypted HTTP. Such redirects occur when one of the following URLs is requested: * URLs in `layouts/alias.html`, e.g. in <https://git-scm.com/docs/> * Image URLs in <https://git-scm.com/application.min.css> * Endpoint URLs in <https://git-scm.com/sitemap.xml> Since these URLs have the `http` scheme, the client is supposed to send the new request unencrypted, only to be told by the server to use HTTPS again. Modern web browsers tend to stick with HTTPS in certain cases, so the downgrade may not always happen, but it's better to eliminate the redirects nevertheless, for security and performance reasons. Instead of trying to use relative URLs everywhere, this commit takes a simpler approach by using the `https` scheme in the base URL. One way to do this is to enable the "Enforce HTTPS" option in the GitHub Pages settings, but it's infeasible because the `git-scm.com` domain points to Cloudflare instead of GitHub. Therefore, this commit introduces a GitHub Actions variable, `EXTERNAL_HTTPS`, which can be set to true if HTTPS is provided by a third party, so that the URL scheme can be safely overridden. This also generalizes the special case of `git-scm.com` for any domain with a similar setup, allowing tests to be run more reliably in a uniform way. See-also: c22a1a5 (deploy(playwright): work around externally-enforced HTTPS, 2024-10-07) See-also: d4f88c1 (deploy: be more careful when auto-upgrading from HTTP -> HTTPS, 2024-10-07)
1 parent 518d2b7 commit 1f478bc

File tree

4 files changed

+50
-14
lines changed

4 files changed

+50
-14
lines changed

.github/actions/deploy-to-github-pages/action.yml

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ inputs:
2020
cloudflare-zone:
2121
description: The Cloudflare zone to purge.
2222
required: false
23+
external-https:
24+
description: Whether HTTPS is set up externally, e.g. on Cloudflare instead of GitHub.
25+
default: false
26+
required: false
2327
outputs:
2428
url:
2529
description: The URL to which the site was deployed
@@ -41,6 +45,15 @@ runs:
4145
id: pages
4246
uses: actions/configure-pages@v5
4347

48+
- name: set base URL
49+
shell: bash
50+
run: |
51+
base_url="${{ steps.pages.outputs.base_url }}"
52+
if [ "${{ inputs.external-https }}" = true ] ; then
53+
base_url="$(echo "$base_url" | sed 's/^http:/https:/')"
54+
fi
55+
echo "base_url=$base_url" >>"$GITHUB_ENV"
56+
4457
- name: configure Hugo and Pagefind version
4558
shell: bash
4659
run: |
@@ -59,7 +72,7 @@ runs:
5972
env:
6073
HUGO_RELATIVEURLS: false
6174
shell: bash
62-
run: hugo config && hugo --baseURL "${{ steps.pages.outputs.base_url }}/"
75+
run: hugo config && hugo --baseURL "${{ env.base_url }}/"
6376

6477
- name: run Pagefind ${{ env.PAGEFIND_VERSION }} to build the search index
6578
shell: bash
@@ -110,7 +123,6 @@ runs:
110123
id: remap
111124
shell: bash
112125
run: |
113-
base_url='${{ steps.pages.outputs.base_url }}'
114126
echo "result=$(echo "$base_url" |
115127
sed 's|^\(.*\)\(/git-scm\.com\)$|(\1)?\2(.*)|') file://$PWD/public\$2" \
116128
>>$GITHUB_OUTPUT
@@ -120,14 +132,25 @@ runs:
120132
sed -n 's|^\(https\?:\/\/.*\)\(/git-scm\.com\)$|--remap '\''(\1.*) file://../$1'\''|p')" \
121133
>>$GITHUB_OUTPUT
122134
135+
- name: check for downgrades to unencrypted HTTP
136+
if: startsWith(env.base_url, 'https://')
137+
shell: bash
138+
# The `--require-https` option of lychee could come in handy,
139+
# but it also complains about `http://` links to other sites,
140+
# and (more importantly) doesn't work in offline mode.
141+
# A simple `grep` should work without any false positives,
142+
# unless git-scm.com mentions the base URL of one of its forks,
143+
# which is unlikely.
144+
run: '! grep -FInr "http:${base_url#https:}" public'
145+
123146
- name: check for broken links
124147
id: lychee
125148
uses: lycheeverse/lychee-action@v2
126149
with:
127150
args: >-
128151
--offline
129152
--fallback-extensions html
130-
--base '${{ steps.pages.outputs.base_url }}'
153+
--base '${{ env.base_url }}'
131154
--remap '${{ steps.remap.outputs.result }}'
132155
${{ steps.remap.outputs.remap-dotdot }}
133156
--exclude file:///path/to/repo.git/
@@ -192,23 +215,21 @@ runs:
192215
- name: Install @playwright/test
193216
shell: bash
194217
run: npm install @playwright/test
195-
- name: Edit /etc/hosts to map git-scm.com to GitHub
218+
- name: Edit /etc/hosts to map the deployed domain to GitHub
196219
shell: bash
197-
# This side-steps the Cloudflare caches
198-
run: sudo sh -c 'echo "185.199.108.153 git-scm.com" >>/etc/hosts'
220+
# This side-steps any caches that might be configured on the domain,
221+
# and works even when the real server is not reachable from GitHub.
222+
run: |
223+
host="$(echo "$base_url" | sed -E 's|^https?://([^:/]+).*|\1|')"
224+
sudo sh -c "echo '185.199.108.153 $host' >>/etc/hosts"
199225
- name: Run Playwright tests
200226
shell: bash
201227
id: playwright
202228
env:
203-
PLAYWRIGHT_TEST_URL: ${{ steps.pages.outputs.base_url }}
204-
run: |
229+
PLAYWRIGHT_TEST_URL: ${{ env.base_url }}
205230
# avoid test failures when HTTPS is enforced half-way through
206-
case "$PLAYWRIGHT_TEST_URL" in
207-
https://*|http://git-scm.com) ;; # okay, leave as-is
208-
http://*) PLAYWRIGHT_TEST_URL="https://${PLAYWRIGHT_TEST_URL#http://}";;
209-
esac &&
210-
echo "result=$PLAYWRIGHT_TEST_URL" >>$GITHUB_OUTPUT &&
211-
npx playwright test --project=chrome
231+
PLAYWRIGHT_EXTERNAL_HTTPS: ${{ inputs.external-https }}
232+
run: npx playwright test --project=chrome
212233
- uses: actions/upload-artifact@v4
213234
if: always() && steps.playwright.outputs.result != ''
214235
with:

.github/workflows/deploy.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,4 @@ jobs:
2727
with:
2828
cloudflare-token: ${{ secrets.CLOUDFLARE_TOKEN }}
2929
cloudflare-zone: ${{ secrets.CLOUDFLARE_ZONE }}
30+
external-https: ${{ vars.EXTERNAL_HTTPS }}

ARCHITECTURE.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,16 @@ With this change, the site can be tested in the fork by pushing to the
3434
`gh-pages` branch (which will trigger the `deploy.yml` workflow) and then
3535
navigating to https://git-scm.<user>.github.io/.
3636

37+
If the site will be deployed to a custom domain that supports HTTPS,
38+
but the "[Enforce HTTPS]" option cannot be enabled in the GitHub Pages settings
39+
(this can happen if the domain points to a third-party gateway),
40+
the [GitHub Actions variable] `EXTERNAL_HTTPS` should be set to `true`,
41+
so that the site can be built with a proper HTTPS base URL.
42+
The official `git-scm.com` domain is an example of such a setup.
43+
44+
[Enforce HTTPS]: https://docs.github.com/en/pages/getting-started-with-github-pages/securing-your-github-pages-site-with-https#enforcing-https-for-your-github-pages-site
45+
[GitHub Actions variable]: https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/store-information-in-variables#defining-configuration-variables-for-multiple-workflows
46+
3747
## Non-static parts
3848

3949
While the site consists mostly of static content, there are a couple of

playwright.config.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ module.exports = defineConfig({
3434

3535
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
3636
trace: 'on-first-retry',
37+
38+
/* Ignore ERR_CERT_COMMON_NAME_INVALID when tesing against GitHub's server,
39+
if the real certificate is hosted by a third party (e.g. Cloudflare). */
40+
ignoreHTTPSErrors: process.env.PLAYWRIGHT_EXTERNAL_HTTPS === 'true',
3741
},
3842

3943
/* Configure projects for major browsers */

0 commit comments

Comments
 (0)