diff --git a/.github/cfp_headers b/.github/cfp_headers new file mode 100644 index 000000000..2252ae372 --- /dev/null +++ b/.github/cfp_headers @@ -0,0 +1,10 @@ +/* + X-XSS-Protection: 1; mode=block + X-Content-Type-Options: nosniff + Strict-Transport-Security: max-age=31536000; includeSubDomains; preload + +/version + Content-Type: text/plain + +/apple-app-site-association + Content-Type: application/json diff --git a/.github/workflows/build_develop.yml b/.github/workflows/build_develop.yml index 56d27c52b..1b3e228ab 100644 --- a/.github/workflows/build_develop.yml +++ b/.github/workflows/build_develop.yml @@ -1,21 +1,25 @@ # Separate to the main build workflow for access to develop # environment secrets, largely similar to build.yaml. -name: Build and Package develop +name: Build and Deploy develop on: push: branches: [ develop ] repository_dispatch: types: [ element-web-notify ] concurrency: - group: ${{ github.workflow }}-${{ github.event.workflow_run.head_branch }} + group: ${{ github.repository_owner }}-${{ github.workflow }}-${{ github.ref_name }} cancel-in-progress: true jobs: build: - name: "Build & Upload source maps to Sentry" + name: "Build & Deploy develop.element.io" # Only respect triggers from our develop branch, ignore that of forks if: github.repository == 'vector-im/element-web' runs-on: ubuntu-latest environment: develop + env: + R2_BUCKET: 'element-web-develop' + R2_URL: ${{ secrets.CF_R2_S3_API }} + R2_PUBLIC_URL: 'https://element-web-develop.element.io' steps: - uses: actions/checkout@v2 @@ -34,11 +38,16 @@ jobs: SENTRY_URL: ${{ secrets.SENTRY_URL }} SENTRY_ORG: element SENTRY_PROJECT: riot-web + # We only deploy the latest bundles to Cloudflare Pages and use _redirects to fallback to R2 for + # older ones. This redirect means that 'self' is insufficient in the CSP, + # and we have to add the R2 URL. + # Once Cloudflare redirects support proxying mode we will be able to ditch this. + # See Proxying in support table at https://developers.cloudflare.com/pages/platform/redirects + CSP_EXTRA_SOURCE: ${{ env.R2_PUBLIC_URL }} - run: mv dist/element-*.tar.gz dist/develop.tar.gz - # We keep the latest develop.tar.gz as the artifact uploaded later expires after 24 and requires auth to download - # Element Desktop's fetch script uses this tarball to fetch latest develop to build Nightlies. + # TODO: remove this once the element-desktop fetch script is updated to grab develop.element.io/develop.tar.gz - name: Deploy develop.tar.gz to Github Pages uses: JamesIves/github-pages-deploy-action@v4 with: @@ -50,12 +59,64 @@ jobs: name: webapp path: dist/develop.tar.gz retention-days: 1 + + - name: Extract webapp + run: | + mkdir _deploy + tar xf dist/develop.tar.gz -C _deploy --strip-components=1 + + - name: Copy config + run: cp element.io/develop/config.json _deploy/config.json + + - name: Populate 404.html + run: echo "404 Not Found" > _deploy/404.html + + - name: Populate _headers + run: cp .github/cfp_headers _deploy/_headers + + # Redirect requests for the develop tarball and the historical bundles to R2 + - name: Populate _redirects + run: | + { + echo "/develop.tar.gz $R2_PUBLIC_URL/develop.tar.gz 301" + for bundle in $(aws s3 ls s3://$R2_BUCKET/bundles/ --endpoint-url $R2_URL --region=auto | awk '{print $2}'); do + echo "/bundles/${bundle}* $R2_PUBLIC_URL/bundles/${bundle}:splat 301" + done + } | tee _deploy/_redirects + env: + AWS_ACCESS_KEY_ID: ${{ secrets.CF_R2_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_TOKEN }} - name: Wait for other steps to succeed uses: lewagon/wait-on-check-action@v1.1.2 with: ref: ${{ github.ref }} - running-workflow-name: 'Build & Upload source maps to Sentry' + running-workflow-name: 'Build & Deploy develop.element.io' repo-token: ${{ secrets.GITHUB_TOKEN }} wait-interval: 10 check-regexp: ^((?!SonarQube|issues|board).)*$ + + # We keep the latest develop.tar.gz on R2 instead of relying on the github artifact uploaded earlier + # as the expires after 24h and requires auth to download. + # Element Desktop's fetch script uses this tarball to fetch latest develop to build Nightlies. + - name: Deploy to R2 + run: | + aws s3 cp dist/develop.tar.gz s3://$R2_BUCKET/develop.tar.gz --endpoint-url $R2_URL --region=auto + aws s3 cp _deploy/bundles s3://$R2_BUCKET/bundles --recursive --endpoint-url $R2_URL --region=auto + env: + AWS_ACCESS_KEY_ID: ${{ secrets.CF_R2_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_TOKEN }} + + - name: Deploy to Cloudflare Pages + id: cfp + uses: cloudflare/pages-action@1 + with: + apiToken: ${{ secrets.CF_PAGES_TOKEN }} + accountId: ${{ secrets.CF_PAGES_ACCOUNT_ID }} + projectName: element-web-develop + directory: _deploy + gitHubToken: ${{ secrets.GITHUB_TOKEN }} + + - run: | + echo "Deployed to ${{ steps.cfp.outputs.url }}" >> $GITHUB_STEP_SUMMARY + diff --git a/res/apple-app-site-association b/res/apple-app-site-association new file mode 100644 index 000000000..8cf662850 --- /dev/null +++ b/res/apple-app-site-association @@ -0,0 +1,18 @@ +{ + "applinks": { + "apps": [], + "details": [ + { + "appID": "7J4U792NQT.im.vector.app", + "paths": [ + "*" + ] + } + ] + }, + "webcredentials": { + "apps": [ + "7J4U792NQT.im.vector.app" + ] + } +} diff --git a/scripts/copy-res.js b/scripts/copy-res.js index 4f53f8f3e..07826b3e8 100755 --- a/scripts/copy-res.js +++ b/scripts/copy-res.js @@ -63,6 +63,7 @@ const INCLUDE_LANGS = [ // common parents. Hence, "res/{a,b}/**": the output will be "dest/a/..." and // "dest/b/...". const COPY_LIST = [ + ["res/apple-app-site-association", "webapp"], ["res/manifest.json", "webapp"], ["res/sw.js", "webapp"], ["res/welcome.html", "webapp"], diff --git a/src/vector/index.html b/src/vector/index.html index 24694fc46..e416ef281 100644 --- a/src/vector/index.html +++ b/src/vector/index.html @@ -25,17 +25,17 @@ <% for (var i=0; i < htmlWebpackPlugin.files.css.length; i++) { var file = htmlWebpackPlugin.files.css[i]; diff --git a/webpack.config.js b/webpack.config.js index a559836b6..7db8d3b62 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -10,6 +10,11 @@ const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); const HtmlWebpackInjectPreload = require('@principalstudio/html-webpack-inject-preload'); const SentryCliPlugin = require("@sentry/webpack-plugin"); +// Environment variables +// RIOT_OG_IMAGE_URL: specifies the URL to the image which should be used for the opengraph logo. +// CSP_EXTRA_SOURCE: specifies a URL which should be appended to each CSP directive which uses 'self', +// this can be helpful if your deployment has redirects for old bundles, such as develop.element.io. + dotenv.config(); let ogImageUrl = process.env.RIOT_OG_IMAGE_URL; if (!ogImageUrl) ogImageUrl = 'https://app.element.io/themes/element/img/logos/opengraph.png'; @@ -587,6 +592,7 @@ module.exports = (env, argv) => { minify: false, templateParameters: { og_image_url: ogImageUrl, + csp_extra_source: process.env.CSP_EXTRA_SOURCE ?? "", }, }),