From bff6c371850b05683885267185a2eaa82216211a Mon Sep 17 00:00:00 2001 From: Matt Hillsdon <44397098+microbit-matt-hillsdon@users.noreply.github.com> Date: Mon, 11 Dec 2023 14:45:57 +0000 Subject: [PATCH] Foundation deployment (#40) I've deleted the upstream workflows rather than disable them for clarity. Will always be easy to reinstate if needed. Non-trivial aspects: - The review/branch/PR previews model we use relies on a working base URL. There a few barriers to this: - A bespoke router redirects you to the root of the review domain so breaking refresh. I've added support for a base URL. - Absolute image paths. You can import images and have the bundler deal with them properly. This might be a bit disruptive though so perhaps we could do upstream - I've done it here for now. Quite a few images are in new UX. - Font paths. They're relative in the `all.min.css` source so not entirely clear what's going wrong. - They work by fluke upstream. In current use `all.min.css` is deployed to the root but references the fonts with ../webfonts/... this works because ../ at the root is ignored but at any other path it breaks. Maybe it was always intended to have a CSS directory? I've done that for now. - There's lots in public/ that will need to have suboptimal cache settings as the bundler isn't aware so output file names don't include a hash. Perhaps one to follow up on before we go live. - I've moved the 3D model from public/assets/model to just model as it wasn't managed by the bundler so can't get the same cache settings as other files in assets. `public/assets` is probably just not a good idea. --- .github/workflows/build.yml | 53 +++++++++++++ .github/workflows/main.yml | 72 ------------------ .github/workflows/production.yml | 71 ----------------- .github/workflows/test.yml | 22 ------ bin/print-ci-env.cjs | 23 ++++++ deployment.cjs | 50 ++++++++++++ index.html | 4 +- public/{ => css}/all.min.css | 0 public/{ => css}/global.css | 0 public/{assets => }/models/license.txt | 0 public/{assets => }/models/microbit.bin | Bin public/{assets => }/models/microbit.gltf | 0 src/App.svelte | 7 +- src/__tests__/license-identifiers.test.ts | 6 +- src/components/3d-inspector/View3DUtility.ts | 7 +- .../ConnectionOfflineWarning.svelte | 2 +- src/components/Gesture.svelte | 3 +- src/components/LoadingSpinner.svelte | 6 +- .../bottom/ConnectedLiveGraphButtons.svelte | 5 +- .../BluetoothConnectingDialog.svelte | 3 +- .../bluetooth/ConnectBatteryDialog.svelte | 3 +- .../bluetooth/ConnectCableDialog.svelte | 3 +- .../bluetooth/StartBluetoothDialog.svelte | 12 ++- .../radio/ConnectCableDialogMB1.svelte | 6 +- .../radio/StartRadioDialog.svelte | 12 ++- .../usb/SelectMicrobitDialogUsb.svelte | 3 +- .../usb/manual/ManualInstallTutorial.svelte | 11 ++- .../RecordInformationContent.svelte | 3 +- .../output/OutputGestureStack.svelte | 9 ++- {public => src}/imgs/InputData.svg | 0 {public => src}/imgs/TestModel.svg | 0 {public => src}/imgs/TrainModel.svg | 0 {public => src}/imgs/blank_microbit.svg | 0 {public => src}/imgs/connect-cable.gif | Bin .../imgs/data-trainer-thumpnail.png | Bin {public => src}/imgs/down_arrow.svg | 0 {public => src}/imgs/led_off.svg | 0 {public => src}/imgs/led_on.svg | 0 {public => src}/imgs/loadingspinner.gif | Bin {public => src}/imgs/microbit-logo.svg | 0 {public => src}/imgs/microbit2_blank.svg | 0 {public => src}/imgs/microbitV1.svg | 0 {public => src}/imgs/microbitV2.svg | 0 {public => src}/imgs/microbit_guide.svg | 0 {public => src}/imgs/microbit_icon.png | Bin {public => src}/imgs/microbit_icon_dual.png | Bin .../imgs/microbit_record_guide.svg | 0 {public => src}/imgs/microbit_unaltered.svg | 0 {public => src}/imgs/model.svg | 0 {public => src}/imgs/model_2.svg | 0 {public => src}/imgs/model_3.svg | 0 {public => src}/imgs/red_arrows.svg | 0 {public => src}/imgs/right_arrow.svg | 0 {public => src}/imgs/right_arrow_blue.svg | 0 {public => src}/imgs/select-microbit.png | Bin {public => src}/imgs/sidebar_background.svg | 0 .../imgs/stylised-battery-pack.svg | 0 .../imgs/stylised-microbit-black.svg | 0 .../imgs/stylised-microbit-connected.svg | 0 {public => src}/imgs/stylised-usb-cable.svg | 0 {public => src}/imgs/stylised_computer.svg | 0 {public => src}/imgs/transfer_firmware.gif | Bin .../imgs/transfer_firmware_chromeos.gif | Bin .../imgs/transfer_firmware_macos.gif | Bin .../imgs/transfer_firmware_windows.gif | Bin {public => src}/imgs/v1.svg | 0 {public => src}/imgs/v2.svg | 0 src/menus/TrainingMenu.svelte | 3 +- src/pages/Homepage.svelte | 17 ++--- src/pages/model/ModelPageStackView.svelte | 3 +- src/router/Router.svelte | 5 +- tsconfig.json | 2 +- vite.config.ts | 3 +- 73 files changed, 207 insertions(+), 222 deletions(-) create mode 100644 .github/workflows/build.yml delete mode 100644 .github/workflows/main.yml delete mode 100644 .github/workflows/production.yml delete mode 100644 .github/workflows/test.yml create mode 100644 bin/print-ci-env.cjs create mode 100644 deployment.cjs rename public/{ => css}/all.min.css (100%) rename public/{ => css}/global.css (100%) rename public/{assets => }/models/license.txt (100%) rename public/{assets => }/models/microbit.bin (100%) rename public/{assets => }/models/microbit.gltf (100%) rename {public => src}/imgs/InputData.svg (100%) rename {public => src}/imgs/TestModel.svg (100%) rename {public => src}/imgs/TrainModel.svg (100%) rename {public => src}/imgs/blank_microbit.svg (100%) rename {public => src}/imgs/connect-cable.gif (100%) rename {public => src}/imgs/data-trainer-thumpnail.png (100%) rename {public => src}/imgs/down_arrow.svg (100%) rename {public => src}/imgs/led_off.svg (100%) rename {public => src}/imgs/led_on.svg (100%) rename {public => src}/imgs/loadingspinner.gif (100%) rename {public => src}/imgs/microbit-logo.svg (100%) rename {public => src}/imgs/microbit2_blank.svg (100%) rename {public => src}/imgs/microbitV1.svg (100%) rename {public => src}/imgs/microbitV2.svg (100%) rename {public => src}/imgs/microbit_guide.svg (100%) rename {public => src}/imgs/microbit_icon.png (100%) rename {public => src}/imgs/microbit_icon_dual.png (100%) rename {public => src}/imgs/microbit_record_guide.svg (100%) rename {public => src}/imgs/microbit_unaltered.svg (100%) rename {public => src}/imgs/model.svg (100%) rename {public => src}/imgs/model_2.svg (100%) rename {public => src}/imgs/model_3.svg (100%) rename {public => src}/imgs/red_arrows.svg (100%) rename {public => src}/imgs/right_arrow.svg (100%) rename {public => src}/imgs/right_arrow_blue.svg (100%) rename {public => src}/imgs/select-microbit.png (100%) rename {public => src}/imgs/sidebar_background.svg (100%) rename {public => src}/imgs/stylised-battery-pack.svg (100%) rename {public => src}/imgs/stylised-microbit-black.svg (100%) rename {public => src}/imgs/stylised-microbit-connected.svg (100%) rename {public => src}/imgs/stylised-usb-cable.svg (100%) rename {public => src}/imgs/stylised_computer.svg (100%) rename {public => src}/imgs/transfer_firmware.gif (100%) rename {public => src}/imgs/transfer_firmware_chromeos.gif (100%) rename {public => src}/imgs/transfer_firmware_macos.gif (100%) rename {public => src}/imgs/transfer_firmware_windows.gif (100%) rename {public => src}/imgs/v1.svg (100%) rename {public => src}/imgs/v2.svg (100%) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..5e507274a --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,53 @@ +name: build + +on: + release: + types: [created] + push: + branches: + - "**" + +concurrency: + group: ${{ github.workflow }}-${{ startsWith(github.ref, 'refs/tags/v') && 'release' || github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' && !startsWith(github.ref, 'refs/tags/v') }} + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + packages: read + env: + AWS_DEFAULT_REGION: eu-west-1 + PRODUCTION_CLOUDFRONT_DISTRIBUTION_ID: NOT_YET + STAGING_CLOUDFRONT_DISTRIBUTION_ID: E37K3V5Y65XQIX + REVIEW_CLOUDFRONT_DISTRIBUTION_ID: E3KUGPF02I4CJ4 + VITE_FOUNDATION_BUILD: ${{ github.repository_owner == 'microbit-foundation' }} + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v3 + with: + node-version: 20 + cache: 'npm' + registry-url: "https://npm.pkg.github.com" + - run: npm ci + - run: npm install --no-save @microbit-foundation/website-deploy-aws@0.6 @microbit-foundation/website-deploy-aws-config@0.9 @microbit-foundation/npm-package-versioner@2 + if: github.repository_owner == 'microbit-foundation' + env: + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - run: npx update-ci-version + - run: node ./bin/print-ci-env.cjs >> $GITHUB_ENV + - run: npm run test + - run: npm run check + - run: npx prettier --check src + - run: npm run build + - run: npx website-deploy-aws + if: github.repository_owner == 'microbit-foundation' && (env.STAGE == 'REVIEW' || success()) + env: + AWS_ACCESS_KEY_ID: ${{ secrets.WEB_DEPLOY_ML_AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.WEB_DEPLOY_ML_AWS_SECRET_ACCESS_KEY }} + - run: npx invalidate-cloudfront-distribution + if: github.repository_owner == 'microbit-foundation' && (env.STAGE == 'REVIEW' || success()) + env: + AWS_ACCESS_KEY_ID: ${{ secrets.WEB_DEPLOY_ML_AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.WEB_DEPLOY_ML_AWS_SECRET_ACCESS_KEY }} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index d08948308..000000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,72 +0,0 @@ -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. - -# GitHub recommends pinning actions to a commit SHA. -# To get a newer version, you will need to update the SHA. -# You can also reference a tag or branch, but the action may change without warning. - -name: Testing deployment - -env: - APP_LOCATION: "/" # location of your client code - APP_ARTIFACT_LOCATION: "/dist" # location of client code build output - -on: - push: - branches: - - main - pull_request: - types: [opened, synchronize, reopened, closed] - branches: - - main - -permissions: - issues: write - contents: read - -jobs: - Jest: - if: false - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Run Tests - run: | - npm install - npm test - build_and_deploy: - if: false - # if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed') - runs-on: ubuntu-latest - name: Build and Deploy - steps: - - uses: actions/checkout@v3 - with: - submodules: true - - name: Branding - run: | - cp -f ./src/__viteBuildVariants__/ml-machine/windi.config.js ./windi.config.js - - name: Build And Deploy - uses: Azure/static-web-apps-deploy@1a947af9992250f3bc2e68ad0754c0b0c11566c9 - with: - azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }} - repo_token: ${{ secrets.GITHUB_TOKEN }} - action: "upload" - app_location: ${{ env.APP_LOCATION }} - app_artifact_location: ${{ env.APP_ARTIFACT_LOCATION }} - env: - VITE_TITLE: ML-Machine - - close: - if: false - # if: github.event_name == 'pull_request' && github.event.action == 'closed' - runs-on: ubuntu-latest - name: Close - steps: - - name: Close - uses: Azure/static-web-apps-deploy@1a947af9992250f3bc2e68ad0754c0b0c11566c9 - with: - azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }} - action: "close" diff --git a/.github/workflows/production.yml b/.github/workflows/production.yml deleted file mode 100644 index abd39d097..000000000 --- a/.github/workflows/production.yml +++ /dev/null @@ -1,71 +0,0 @@ -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. - -# GitHub recommends pinning actions to a commit SHA. -# To get a newer version, you will need to update the SHA. -# You can also reference a tag or branch, but the action may change without warning. - -name: Production deployment - -env: - APP_LOCATION: "/" # location of your client code - APP_ARTIFACT_LOCATION: "/dist" # location of client code build output - -on: - push: - branches: - - production - pull_request: - types: [opened, synchronize, reopened, closed] - branches: - - production - -permissions: - issues: write - contents: read - -jobs: - Jest: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Run Tests - run: | - npm install - npm test - build_and_deploy: - if: false - # if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed') - runs-on: ubuntu-latest - name: Build and Deploy - steps: - - uses: actions/checkout@v3 - with: - submodules: true - - name: Branding - run: | - cp -f ./src/__viteBuildVariants__/ml-machine/windi.config.js ./windi.config.js - - name: Build And Deploy - uses: Azure/static-web-apps-deploy@1a947af9992250f3bc2e68ad0754c0b0c11566c9 - with: - azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_PRODUCTION_TOKEN }} - repo_token: ${{ secrets.GITHUB_TOKEN }} - action: "upload" - app_location: ${{ env.APP_LOCATION }} - app_artifact_location: ${{ env.APP_ARTIFACT_LOCATION }} - env: - VITE_TITLE: ML-Machine - - close: - if: false - # if: github.event_name == 'pull_request' && github.event.action == 'closed' - runs-on: ubuntu-latest - name: Close - steps: - - name: Close - uses: Azure/static-web-apps-deploy@1a947af9992250f3bc2e68ad0754c0b0c11566c9 - with: - azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_PRODUCTION_TOKEN }} - action: "close" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index d6ab866d4..000000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Run tests & build - -on: - push: - branches: '*' - pull_request: - branches: '*' - -jobs: - test-and-build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v3 - with: - node-version: 20 - cache: 'npm' - - run: npm install - - run: npm run test - - run: npm run check - - run: npx prettier --check src - - run: npm run build diff --git a/bin/print-ci-env.cjs b/bin/print-ci-env.cjs new file mode 100644 index 000000000..2f33d7755 --- /dev/null +++ b/bin/print-ci-env.cjs @@ -0,0 +1,23 @@ +#!/usr/bin/env node + +const ref = process.env.GITHUB_REF; +let stage; +if (ref === 'refs/heads/main') { + stage = 'STAGING'; +} else if (ref.startsWith('refs/tags/v')) { + stage = 'PRODUCTION'; +} else { + stage = 'REVIEW'; +} + +process.env.STAGE = stage; +// STAGE must be defined before this is imported +const { bucketName, bucketPrefix } = require('../deployment.cjs'); +const baseUrl = !bucketPrefix ? '/' : `/${bucketPrefix}/`; +const fullUrl = `https://${bucketName}${baseUrl}`; + +console.log(`STAGE=${stage}`); +console.log(`VITE_STAGE=${stage}`); +console.log(`BASE_URL=${baseUrl}`); +// This can be used for og:url and similar. Not quite right for review domain but we don't really care. +console.log(`VITE_FULL_URL=${fullUrl}`); diff --git a/deployment.cjs b/deployment.cjs new file mode 100644 index 000000000..342be4a3b --- /dev/null +++ b/deployment.cjs @@ -0,0 +1,50 @@ +const { + createDeploymentDetailsFromOptions, +} = require('@microbit-foundation/website-deploy-aws-config'); + +const { s3Config } = createDeploymentDetailsFromOptions({ + production: { + bucket: 'ml.microbit.org', + mode: 'root', + allowPrerelease: false, + }, + staging: { + bucket: 'stage-ml.microbit.org', + }, + review: { + bucket: 'review-ml.microbit.org', + mode: 'branch-prefix', + }, +}); + +module.exports = { + deploymentDir: './dist', + ...s3Config, + region: 'eu-west-1', + removeNonexistentObjects: true, + enableS3StaticWebsiteHosting: true, + errorDocumentKey: 'index.html', + redirects: [], + params: { + '**/**.html': { + CacheControl: 'public, max-age=0, must-revalidate', + }, + '**/assets/**': { CacheControl: 'public, max-age=31536000, immutable' }, + // There's lots in public/ that we'd ideally use the bundler for to improve caching + 'css/**': { + CacheControl: 'public, max-age=0, must-revalidate', + }, + 'firmware/**': { + CacheControl: 'public, max-age=0, must-revalidate', + }, + 'imgs/**': { + CacheControl: 'public, max-age=0, must-revalidate', + }, + 'sounds/**': { + CacheControl: 'public, max-age=0, must-revalidate', + }, + 'webfonts/**': { + CacheControl: 'public, max-age=0, must-revalidate', + }, + }, +}; diff --git a/index.html b/index.html index 0aba02bf2..e97246757 100644 --- a/index.html +++ b/index.html @@ -12,8 +12,8 @@  - - + + +
- loading + loading
diff --git a/src/components/bottom/ConnectedLiveGraphButtons.svelte b/src/components/bottom/ConnectedLiveGraphButtons.svelte index b729c2ea7..9a662e09e 100644 --- a/src/components/bottom/ConnectedLiveGraphButtons.svelte +++ b/src/components/bottom/ConnectedLiveGraphButtons.svelte @@ -9,6 +9,7 @@ import TypingUtils from '../../script/TypingUtils'; import { state } from '../../script/stores/uiStore'; import StandardButton from '../StandardButton.svelte'; + import loadingSpinnerImage from '../../imgs/loadingspinner.gif'; export let onOutputDisconnectButtonClicked: () => void; export let onInputDisconnectButtonClicked: () => void; @@ -25,7 +26,7 @@ >{$t('menu.model.disconnect')} {:else} - loading + loading {/if} {/if} @@ -37,7 +38,7 @@ >{$t('footer.disconnectButton')} {:else} - loading + loading {/if} diff --git a/src/components/connection-prompt/bluetooth/BluetoothConnectingDialog.svelte b/src/components/connection-prompt/bluetooth/BluetoothConnectingDialog.svelte index cf0abba94..9d4c93123 100644 --- a/src/components/connection-prompt/bluetooth/BluetoothConnectingDialog.svelte +++ b/src/components/connection-prompt/bluetooth/BluetoothConnectingDialog.svelte @@ -19,6 +19,7 @@ import { DeviceRequestStates } from '../../../script/stores/connectDialogStore'; import Environment from '../../../script/Environment'; import StaticConfiguration from '../../../StaticConfiguration'; + import loadingSpinnerImage from '../../../imgs/loadingspinner.gif'; // callbacks export let deviceState: DeviceRequestStates; @@ -114,7 +115,7 @@

{$t('connectMB.bluetooth.connecting')}

- loading + loading
{/if} diff --git a/src/components/connection-prompt/bluetooth/ConnectBatteryDialog.svelte b/src/components/connection-prompt/bluetooth/ConnectBatteryDialog.svelte index 034473e44..b1e4f7a97 100644 --- a/src/components/connection-prompt/bluetooth/ConnectBatteryDialog.svelte +++ b/src/components/connection-prompt/bluetooth/ConnectBatteryDialog.svelte @@ -7,6 +7,7 @@
@@ -16,7 +17,7 @@