diff --git a/.cloudbuild/staging-check.js b/.cloudbuild/staging-check.js new file mode 100644 index 000000000..8025827f5 --- /dev/null +++ b/.cloudbuild/staging-check.js @@ -0,0 +1,79 @@ +#!/usr/bin/env node +/** + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview This file compares the current commit hash against the staging site. If they're + * different, and no other build tasks are running, we kick off "staging-deploy". + */ + +const { ErrorReporting } = require('@google-cloud/error-reporting'); +const { CloudBuildClient } = require('@google-cloud/cloudbuild'); +const { getDeployedVersion, getCurrentVersion } = require('../build/git-version.js'); + +const client = new CloudBuildClient(); +const errors = new ErrorReporting(); + +// This is the trigger ID of "staging-deploy" for "santa-staging". +const deployTriggerId = 'd6401587-de8b-4507-ae71-bc516fdfc64a'; + +(async () => { + const deployedVersion = await getDeployedVersion(); + const currentVersion = await getCurrentVersion(); + console.log(`version deployed="${deployedVersion}" local="${currentVersion}""`); + + if (deployedVersion && deployedVersion === currentVersion) { + console.log( + 'The current and deployed versions are the same, not continuing build.' + ); + return; + } + + console.log( + 'The current and deployed versions are different, kicking off deploy build.' + ); + + // Check if there are any existing builds. + const ret = client.listBuildsAsync({ + projectId: process.env.PROJECT_ID, + pageSize: 1, + filter: `trigger_id="${deployTriggerId}" AND (status="WORKING" OR status="QUEUED")`, + }); + + // This is an async iterable, check if we have at least one, if so, there's an active build. + let activeBuild = false; + for await (const _build of ret) { + activeBuild = true; + break; + } + if (activeBuild) { + console.log( + 'There is a current active or queued build. Not starting another.' + ); + return; + } + + try { + // This just waits for the build to be kicked off, not for its completion (it + // returns a LROperation). + await client.runBuildTrigger({ + projectId: process.env.PROJECT_ID, + triggerId: deployTriggerId, + }); + } catch (e) { + errors.report(e); + } +})(); diff --git a/.cloudbuild/staging-check.yaml b/.cloudbuild/staging-check.yaml new file mode 100644 index 000000000..f5b403a99 --- /dev/null +++ b/.cloudbuild/staging-check.yaml @@ -0,0 +1,16 @@ +# This Cloud Build task can kick off the staging deploy if the hash has changed. + +steps: + - name: node + id: 'Install dependencies' + entrypoint: npm + args: ['ci'] + + - name: node + id: 'Verify and maybe kick off build for new version' + entrypoint: npm + args: ['run', 'staging-check'] + +options: + env: + - 'PROJECT_ID=$PROJECT_ID' diff --git a/.cloudbuild/staging-deploy.sh b/.cloudbuild/staging-deploy.sh new file mode 100644 index 000000000..f6f066585 --- /dev/null +++ b/.cloudbuild/staging-deploy.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +set -eu + +BASEURL="https://santa-staging.firebaseapp.com/" + +# move to the root directory of santa-tracker-web +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +STAGING_ROOT="$ROOT/staging" +cd $ROOT/.. + +# build! +node ./release.js --baseurl=$BASEURL --minify=false + +# move prod to GaE skeleton +rm -rf $STAGING_ROOT/appengine/prod +mv dist/prod $STAGING_ROOT/appengine/prod + +# move static to firebase +# Note that when we deploy, we clobber the "old" deploy. People mucking with the staging site will +# be suddenly surprised when they can't load content! +mkdir -p $STAGING_ROOT/firebase/public +mv dist/_static/* $STAGING_ROOT/firebase/public diff --git a/.cloudbuild/staging-deploy.yaml b/.cloudbuild/staging-deploy.yaml new file mode 100644 index 000000000..66109b49a --- /dev/null +++ b/.cloudbuild/staging-deploy.yaml @@ -0,0 +1,36 @@ +# This Cloud Build task runs the deploy to staging. + +steps: + - name: node + id: 'Install dependencies' + entrypoint: npm + args: ['ci'] + + # This is an optional dependency of "google-closure-compiler", but it doesn't always install on + # Cloud Build for some reason. We need this as we can't use the Java compiler in the Node image. + - name: node + id: 'Force install Closure native Linux binary' + entrypoint: 'npm' + args: ['install', 'google-closure-compiler-linux'] + + - name: node + id: 'Build' + entrypoint: bash + args: ['.cloudbuild/staging-deploy.sh'] + + - name: 'gcr.io/$PROJECT_ID/firebase' + dir: '.cloudbuild/staging/firebase' + args: ['deploy', '--only', 'hosting', '--project', 'santa-staging'] + + - name: 'gcr.io/cloud-builders/gcloud' + dir: '.cloudbuild/staging/appengine' + entrypoint: 'bash' + args: ['-c', 'gcloud app deploy --version hohoho --project santa-staging'] + +options: + machineType: 'E2_HIGHCPU_32' # yolo + env: + - 'PROJECT_ID=$PROJECT_ID' + - 'NODE_OPTIONS="--max-old-space-size=32768"' + +timeout: 1800s diff --git a/.cloudbuild/staging/.gitignore b/.cloudbuild/staging/.gitignore new file mode 100644 index 000000000..159086083 --- /dev/null +++ b/.cloudbuild/staging/.gitignore @@ -0,0 +1,2 @@ +appengine/prod +firebase/public diff --git a/.cloudbuild/staging/appengine/app.yaml b/.cloudbuild/staging/appengine/app.yaml new file mode 100644 index 000000000..25f3f0375 --- /dev/null +++ b/.cloudbuild/staging/appengine/app.yaml @@ -0,0 +1,6 @@ +runtime: go122 + +handlers: +- url: /.* + script: auto + secure: always diff --git a/.cloudbuild/staging/appengine/go.mod b/.cloudbuild/staging/appengine/go.mod new file mode 100644 index 000000000..166c6c5b7 --- /dev/null +++ b/.cloudbuild/staging/appengine/go.mod @@ -0,0 +1,3 @@ +module github.com/google/santa-tracker-web + +go 1.15 diff --git a/.cloudbuild/staging/appengine/go.sum b/.cloudbuild/staging/appengine/go.sum new file mode 100644 index 000000000..e69de29bb diff --git a/.cloudbuild/staging/appengine/http.go b/.cloudbuild/staging/appengine/http.go new file mode 100644 index 000000000..5f50a4c1e --- /dev/null +++ b/.cloudbuild/staging/appengine/http.go @@ -0,0 +1,53 @@ +package main + +import ( + "fmt" + "log" + "net/http" + "os" + "regexp" +) + +var ( + intlMatcher = regexp.MustCompile(`^/intl/([-_\w]+)(/.*|)$`) +) + +func main() { + fileServer := http.FileServer(http.Dir("prod")) + handler := rewriteLang(fileServer) + + http.Handle("/", handler) + + port := os.Getenv("PORT") + if port == "" { + port = "8080" + } + addr := ":" + port + log.Printf("Serving on %s...", addr) + http.ListenAndServe(addr, nil) +} + +// pathForLangFile serves the specified "rest" file for the given lang, falling +// back to the top-level if it doesn't exist there. +func pathForLangFile(lang, rest string) string { + if len(rest) != 0 && rest[0] == '/' { + rest = rest[1:] // trim leading slash + } + check := fmt.Sprintf("prod/intl/%s_ALL/%s", lang, rest) + if _, err := os.Stat(check); err != nil && os.IsNotExist(err) { + return fmt.Sprintf("/%s", rest) + } + return fmt.Sprintf("/intl/%s_ALL/%s", lang, rest) +} + +func rewriteLang(wrapped http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + found := intlMatcher.FindStringSubmatch(r.URL.Path) + if found != nil { + // this was an /intl/de/... request, rewrite it + lang, rest := found[1], found[2] + r.URL.Path = pathForLangFile(lang, rest) + } + wrapped.ServeHTTP(w, r) + }) +} diff --git a/.cloudbuild/staging/firebase/firebase.json b/.cloudbuild/staging/firebase/firebase.json new file mode 100644 index 000000000..1a627570c --- /dev/null +++ b/.cloudbuild/staging/firebase/firebase.json @@ -0,0 +1,20 @@ +{ + "hosting": { + "public": "public", + "ignore": [ + "firebase.json", + "**/.*" + ], + "headers": [ + { + "source": "**", + "headers": [ + { + "key": "Access-Control-Allow-Origin", + "value": "*" + } + ] + } + ] + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 6f2de4f9d..7c9c93494 100644 --- a/.gitignore +++ b/.gitignore @@ -5,5 +5,5 @@ node_modules/ .firebaserc .firebase/ firebase-debug.log -package-lock.json +yarn.lock plan8-klang-sync.js diff --git a/LICENSE b/LICENSE index 02111f627..a09917c07 100644 --- a/LICENSE +++ b/LICENSE @@ -1,12 +1,12 @@ All image and audio files (including *.png, *.jpg, *.svg, *.mp3, and *.ogg) are -licensed under the CC-BY-NC license. All other files are licensed under the +licensed under the CC-BY license. All other files are licensed under the Apache 2 license. -CC-BY-NC License +CC-BY License ---------------- -Attribution-NonCommercial-ShareAlike 4.0 International +Attribution 4.0 International ======================================================================= @@ -41,7 +41,7 @@ exhaustive, and do not form part of our licenses. material not subject to the license. This includes other CC- licensed material, or material used under an exception or limitation to copyright. More considerations for licensors: - wiki.creativecommons.org/Considerations_for_licensors + wiki.creativecommons.org/Considerations_for_licensors Considerations for the public: By using one of our public licenses, a licensor grants the public permission to use the @@ -56,24 +56,22 @@ exhaustive, and do not form part of our licenses. rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to - respect those requests where reasonable. More_considerations - for the public: - wiki.creativecommons.org/Considerations_for_licensees + respect those requests where reasonable. More considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees ======================================================================= -Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International -Public License +Creative Commons Attribution 4.0 International Public License By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons -Attribution-NonCommercial-ShareAlike 4.0 International Public License -("Public License"). To the extent this Public License may be -interpreted as a contract, You are granted the Licensed Rights in -consideration of Your acceptance of these terms and conditions, and the -Licensor grants You such rights in consideration of benefits the -Licensor receives from making the Licensed Material available under -these terms and conditions. +Attribution 4.0 International Public License ("Public License"). To the +extent this Public License may be interpreted as a contract, You are +granted the Licensed Rights in consideration of Your acceptance of +these terms and conditions, and the Licensor grants You such rights in +consideration of benefits the Licensor receives from making the +Licensed Material available under these terms and conditions. Section 1 -- Definitions. @@ -92,11 +90,7 @@ Section 1 -- Definitions. and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. - c. BY-NC-SA Compatible License means a license listed at - creativecommons.org/compatiblelicenses, approved by Creative - Commons as essentially the equivalent of this Public License. - - d. Copyright and Similar Rights means copyright and/or similar rights + c. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or @@ -104,41 +98,29 @@ Section 1 -- Definitions. specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. - e. Effective Technological Measures means those measures that, in the + d. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. - f. Exceptions and Limitations means fair use, fair dealing, and/or + e. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. - g. License Elements means the license attributes listed in the name - of a Creative Commons Public License. The License Elements of this - Public License are Attribution, NonCommercial, and ShareAlike. - - h. Licensed Material means the artistic or literary work, database, + f. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License. - i. Licensed Rights means the rights granted to You subject to the + g. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. - j. Licensor means the individual(s) or entity(ies) granting rights + h. Licensor means the individual(s) or entity(ies) granting rights under this Public License. - k. NonCommercial means not primarily intended for or directed towards - commercial advantage or monetary compensation. For purposes of - this Public License, the exchange of the Licensed Material for - other material subject to Copyright and Similar Rights by digital - file-sharing or similar means is NonCommercial provided there is - no payment of monetary compensation in connection with the - exchange. - - l. Share means to provide material to the public by any means or + i. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material @@ -146,13 +128,13 @@ Section 1 -- Definitions. public may access the material from a place and at a time individually chosen by them. - m. Sui Generis Database Rights means rights other than copyright + j. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. - n. You means the individual or entity exercising the Licensed Rights + k. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. @@ -166,10 +148,9 @@ Section 2 -- Scope. exercise the Licensed Rights in the Licensed Material to: a. reproduce and Share the Licensed Material, in whole or - in part, for NonCommercial purposes only; and + in part; and - b. produce, reproduce, and Share Adapted Material for - NonCommercial purposes only. + b. produce, reproduce, and Share Adapted Material. 2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public @@ -199,13 +180,7 @@ Section 2 -- Scope. Licensed Rights under the terms and conditions of this Public License. - b. Additional offer from the Licensor -- Adapted Material. - Every recipient of Adapted Material from You - automatically receives an offer from the Licensor to - exercise the Licensed Rights in the Adapted Material - under the conditions of the Adapter's License You apply. - - c. No downstream restrictions. You may not offer or impose + b. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the @@ -237,9 +212,7 @@ Section 2 -- Scope. Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly - reserves any right to collect such royalties, including when - the Licensed Material is used other than for NonCommercial - purposes. + reserves any right to collect such royalties. Section 3 -- License Conditions. @@ -284,28 +257,14 @@ following conditions. reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. + 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. - b. ShareAlike. - - In addition to the conditions in Section 3(a), if You Share - Adapted Material You produce, the following conditions also apply. - - 1. The Adapter's License You apply must be a Creative Commons - license with the same License Elements, this version or - later, or a BY-NC-SA Compatible License. - - 2. You must include the text of, or the URI or hyperlink to, the - Adapter's License You apply. You may satisfy this condition - in any reasonable manner based on the medium, means, and - context in which You Share Adapted Material. - - 3. You may not offer or impose any additional or different terms - or conditions on, or apply any Effective Technological - Measures to, Adapted Material that restrict exercise of the - rights granted under the Adapter's License You apply. + 4. If You Share Adapted Material You produce, the Adapter's + License You apply must not prevent recipients of the Adapted + Material from complying with this Public License. Section 4 -- Sui Generis Database Rights. @@ -315,14 +274,12 @@ apply to Your use of the Licensed Material: a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial - portion of the contents of the database for NonCommercial purposes - only; + portion of the contents of the database; b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database - Rights (but not its individual contents) is Adapted Material, - including for purposes of Section 3(b); and + Rights (but not its individual contents) is Adapted Material; and c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. @@ -423,13 +380,16 @@ Section 8 -- Interpretation. that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. + ======================================================================= -Creative Commons is not a party to its public licenses. -Notwithstanding, Creative Commons may elect to apply one of its public -licenses to material it publishes and in those instances will be -considered the "Licensor." Except for the limited purpose of indicating -that material is shared under a Creative Commons public license or as +Creative Commons is not a party to its public +licenses. Notwithstanding, Creative Commons may elect to apply one of +its public licenses to material it publishes and in those instances +will be considered the “Licensor.” The text of the Creative Commons +public licenses is dedicated to the public domain under the CC0 Public +Domain Dedication. Except for the limited purpose of indicating that +material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark "Creative Commons" or any other trademark or logo @@ -437,8 +397,8 @@ of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For -the avoidance of doubt, this paragraph does not form part of the public -licenses. +the avoidance of doubt, this paragraph does not form part of the +public licenses. Creative Commons may be contacted at creativecommons.org. diff --git a/README.md b/README.md index 4749ad987..b4faf1cac 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,12 @@ # Google Santa Tracker for Web This repository contains the code to [Google Santa Tracker](https://santatracker.google.com), an educational and entertaining tradition for the December holiday period. -Santa Tracker is built by Developer Relations within Google. We hope you find this source code interesting. In general, we do not accept external contributions from the public. -You can file bug reports or feature requests, or contact the engineering lead [@samthor](https://twitter.com/samthor). +You can file bug reports or feature requests, or contact the engineering lead [Jez Swanson](https://twitter.com/jezzamonn). + +(This text duplicated in [contributing.md](docs/contributing.md)) ## Supports @@ -14,22 +15,26 @@ It also supports other Chromium-based browsers (Edge, Opera etc). We also present a "fallback mode" for older browsers, such as IE11, which allow users to play a small number of historic games. -# Run +# Site Structure + +Santa Tracker is split up into different scenes. Each page on the Santa Tracker corresponds to one scene, including the main village page, [modvil](static/scenes/modvil/index.html). The scenes are in the [static/scenes/](static/scenes/) directory. Each scene is loaded as an iframe, and is relatively self contained. + +The host part of the site handles the loading of each scene, as well as the music and common UI, like the game score or tutorial. There's an [API](static/src/scene/api.js) between the host and the scenes, which allows the host to notify the scenes when events like the scene loading happens, and allows the scenes to tell the host to do things like play a song or update the score. + +# Development Guide + +## Running locally You'll need `yarn` or `npm`. You may also need Java if you're building on Windows, as the binary version of Closure Compiler is unsupported on that platform. Clone and run `yarn` or `npm install` to install deps, and run `./serve.js` to run a development server. The development URL will be copied to your clipboard. -Have fun! 🎅🎄🎁 - -## Development -The serving script `./serve.js` will listen on both ports 8000 and 8080 by default. -The lower port serves the contents of `prod/`, which provides the "host" which fundamentally loads scenes in frames (this matches the production https://santatracker.google.com domain). +The serving script `./serve.js` will listen on both ports 8000 and 8080 by default. Port 8000 serves the host part of the site (this corresponds to the production https://santatracker.google.com domain), and port 8080 serves the static content, including the scenes. To load a specific scene, open e.g., http://localhost:8000/boatload.html. -Once the site is loaded, you can also run `santaApp.route = 'sceneName'` in the console to switch scenes programatically. +Once the site is loaded, you can also run `santaApp.route = 'sceneName'` in the console to switch scenes programmatically. If you'd like to load a scene from the static domain—without the "host" code—you can load it at e.g., http://127.0.0.1:8080/st/scenes/elfmaker/. This is intentionally not equal to "localhost" so that prod and static run cross-domain. @@ -39,39 +44,6 @@ As of 2020, development requires Chrome or a Chromium-based browser. This is due to the way we identify ESM import requests, where Chromium specifies additional headers. (This is a bug, not a feature.) -## Production - -While the source code includes a release script, it's not intended for end-users to run and is used by Googlers to deploy the site. - - - -# Historic Versions - -The previous version of Santa Tracker, used until 2018, is available in the [archive-2018](https://github.com/google/santa-tracker-web/tree/archive-2018) branch. - -# Development Guide - ## Add A New Scene Scenes are fundamentally just pages loaded in an `