Skip to content

Commit c43d199

Browse files
committed
chore: add deployment CI to OVH
1 parent a164178 commit c43d199

2 files changed

Lines changed: 167 additions & 0 deletions

File tree

.github/workflows/deploy.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: Deployment
2+
3+
on:
4+
push:
5+
branches:
6+
- website
7+
- chore/add-ovh-deployment
8+
pull_request:
9+
types: [opened, synchronize, reopened, closed, labeled]
10+
workflow_dispatch:
11+
12+
jobs:
13+
deploy:
14+
if: github.event_name == 'push' || (github.event.action != 'closed' && github.event.action != 'labeled')
15+
uses: ./.github/workflows/ovh.yaml
16+
with:
17+
environment_name: ${{ github.event_name == 'push' && 'production' || format('pull/{0}', github.event.pull_request.number) }}
18+
environment_url: https://mixxx.acolombier.dev${{ github.event_name == 'pull_request' && format('/preview/pull/{0}', github.event.pull_request.number) || '' }}
19+
target: ${{ github.event_name == 'pull_request' && format('preview/pull/{0}', github.event.pull_request.number) || '' }}
20+
base: ${{ github.event_name == 'pull_request' && format('/preview/pull/{0}', github.event.pull_request.number) || '' }}
21+
action: deploy
22+
secrets: inherit
23+
24+
cleanup:
25+
if: |
26+
github.event_name == 'pull_request' && (github.event.action == 'closed' || contains(github.event.pull_request.labels.*.name, 'stale'))
27+
uses: ./.github/workflows/ovh.yaml
28+
with:
29+
environment_name: ${{ format('pull/{0}', github.event.pull_request.number) }}
30+
target: ${{ format('preview/pull/{0}', github.event.pull_request.number) }}
31+
action: teardown
32+
secrets: inherit

.github/workflows/ovh.yaml

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
name: Reusable Deploy
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
environment_name:
7+
required: true
8+
type: string
9+
environment_url:
10+
required: false
11+
type: string
12+
target:
13+
required: false
14+
type: string
15+
default: 'www'
16+
base:
17+
required: false
18+
type: string
19+
default: ''
20+
action:
21+
required: false
22+
type: string
23+
default: 'deploy' # Or 'teardown'
24+
secrets:
25+
OVH_USERNAME:
26+
required: true
27+
OVH_HOSTNAME:
28+
required: true
29+
OVH_PASSWORD:
30+
required: true
31+
32+
jobs:
33+
apply:
34+
runs-on: ubuntu-latest
35+
concurrency:
36+
group: website-ovh
37+
environment:
38+
name: ${{ inputs.environment_name }}
39+
url: ${{ inputs.environment_url }}
40+
steps:
41+
- name: Check out repository
42+
if: ${{ inputs.action != 'teardown' }}
43+
uses: actions/checkout@v4.2.2
44+
with:
45+
fetch-depth: 0
46+
- name: Set up Python
47+
if: ${{ inputs.action != 'teardown' }}
48+
uses: actions/setup-python@v6
49+
with:
50+
python-version: 3.12
51+
cache: 'pip' # caching pip dependencies
52+
- name: Install Python dependencies
53+
if: ${{ inputs.action != 'teardown' }}
54+
run: |
55+
pip install -r requirements.txt
56+
- name: Install dependencies
57+
run: |
58+
sudo apt-get update
59+
sudo apt-get install -y sshfs
60+
- name: Build
61+
if: ${{ inputs.action != 'teardown' }}
62+
env:
63+
BASE: ${{ inputs.base }}
64+
run: |
65+
pelican -e SITEURL="\"$BASE\""
66+
sed -E '/\{% (end)?verbatim %\}/d' htaccess > ./output/.htaccess
67+
# Migrated from netlify.toml
68+
cat >> ./output/.htaccess <<EOF
69+
<IfModule mod_headers.c>
70+
# Global security headers
71+
Header set Content-Security-Policy "default-src 'self'; style-src 'self'; script-src 'self'"
72+
Header set Permissions-Policy "ambient-light-sensor=(); autoplay=(); accelerometer=(); camera=(); display-capture=(); document-domain=(); encrypted-media=(); fullscreen=(); gyroscope=(); magnetometer=(); microphone=(); midi=(); payment=(); picture-in-picture=(); sync-xhr=(); usb=(); wake-lock=(); xr-spatial-tracking=()"
73+
Header set Referrer-Policy "no-referrer-when-downgrade"
74+
Header set X-Content-Type-Options "nosniff"
75+
Header set X-Frame-Options "DENY"
76+
Header set X-XSS-Protection "1; mode=block"
77+
78+
# Override CSP for /news/* pages
79+
SetEnvIf Request_URI "^/news/" is_news
80+
Header set Content-Security-Policy "default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'sha256-UPkidoMErzWw1gW/eY4LhAi9ZkPch3PP31d6KQoJ6Yc=' 'sha256-G40wI6OaLZXCtrb02xUq1H1kEVWjstzoQ0FXKwsWxPw=' 'sha256-1CldwzdEg2k1wTmf7s5RWVd7NMXI/7nxxjJM2C4DqII=' https://mixxx.discourse.group/javascripts/embed.js *.discourse-cdn.com; frame-src 'self' https://www.youtube-nocookie.com https://mixxx.discourse.group ; img-src 'self' https://i.ytimg.com https://raw.githubusercontent.com/mixxxdj/ ; connect-src 'self' https://mixxx.discourse.group https://*.discourse-cdn.com" env=is_news
81+
</IfModule>
82+
EOF
83+
sed -E '/\{% (end)?verbatim %\}/d' content/robots.txt > ./output/robots.txt
84+
- name: Deploy website
85+
if: ${{ inputs.action != 'teardown' }}
86+
env:
87+
OVH_USERNAME: ${{ secrets.OVH_USERNAME }}
88+
OVH_HOSTNAME: ${{ secrets.OVH_HOSTNAME }}
89+
OVH_PASSWORD: ${{ secrets.OVH_PASSWORD }}
90+
TARGET: ${{ inputs.target }}
91+
run: |
92+
set -exo pipefail
93+
mkdir -p ${HOME}/.ssh/
94+
mkdir -p /tmp/ovh_sshfs
95+
ssh-keyscan "${OVH_HOSTNAME}" >> "${HOME}/.ssh/known_hosts"
96+
sshfs "${OVH_USERNAME}@${OVH_HOSTNAME}:/home/${OVH_USERNAME}" \
97+
/tmp/ovh_sshfs \
98+
-o ssh_command="sshpass -p ${OVH_PASSWORD} ssh" \
99+
-o kernel_cache \
100+
-o attr_timeout=600 \
101+
-o entry_timeout=600 \
102+
-o negative_timeout=600 \
103+
-o compression=no \
104+
-o PreferredAuthentications=password \
105+
-o Ciphers=aes128-gcm@openssh.com \
106+
-o reconnect,ServerAliveInterval=15,ServerAliveCountMax=3 \
107+
-o max_conns=4
108+
if [ ! "/tmp/ovh_sshfs/www/" = "/tmp/ovh_sshfs/www/${TARGET}" ] && [ ! -d "/tmp/ovh_sshfs/www/${TARGET}" ]; then
109+
pushd /tmp/ovh_sshfs/www
110+
find . -not \( -path './preview*' -prune \) -type d | xargs -P32 -I{} mkdir -p "./${TARGET}/{}"
111+
find . -not \( -path './preview*' -prune \) -not -type d | xargs -P32 -I{} ln -s /tmp/ovh_sshfs/www/{} "/tmp/ovh_sshfs/www/${TARGET}/{}"
112+
popd
113+
rsync --verbose --recursive --checksum --delete --delay-updates --link-dest="/tmp/ovh_sshfs/www" ./output/ "/tmp/ovh_sshfs/www/${TARGET}"
114+
else
115+
rsync --verbose --recursive --checksum --delete --delay-updates --exclude=preview ./output/ "/tmp/ovh_sshfs/www/${TARGET}"
116+
fi
117+
fusermount -u /tmp/ovh_sshfs
118+
- name: Delete website
119+
if: ${{ inputs.action == 'teardown' }}
120+
env:
121+
OVH_USERNAME: ${{ secrets.OVH_USERNAME }}
122+
OVH_HOSTNAME: ${{ secrets.OVH_HOSTNAME }}
123+
OVH_PASSWORD: ${{ secrets.OVH_PASSWORD }}
124+
TARGET: ${{ inputs.target }}
125+
run: |
126+
set -exo pipefail
127+
mkdir -p ${HOME}/.ssh/
128+
mkdir -p /tmp/ovh_sshfs
129+
ssh-keyscan "${OVH_HOSTNAME}" >> "${HOME}/.ssh/known_hosts"
130+
sshfs "${OVH_USERNAME}@${OVH_HOSTNAME}:/home/${OVH_USERNAME}" \
131+
/tmp/ovh_sshfs \
132+
-o ssh_command="sshpass -p ${OVH_PASSWORD} ssh -o PreferredAuthentications=password" \
133+
-o max_conns=16 &
134+
rm -rf "/tmp/ovh_sshfs/www/${TARGET}"
135+
kill %1

0 commit comments

Comments
 (0)