Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add support for deploying Telescope using spa-tools #273

Merged
merged 7 commits into from
Feb 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ jobs:
if: steps.s3-cache.outputs.processed == 'false'
run: ${{ inputs.build_cmd }}
env:
NOTION_API_KEY: ${{ secrets.NOTION_API_KEY }}
TURBO_TOKEN: ${{ inputs.turbo_cache && secrets.TURBO_TOKEN || null }}
TURBO_FORCE: ${{ inputs.turbo_force }}

Expand Down
15 changes: 13 additions & 2 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ on:
deployment_url:
description: "URL where the deployment can be accessed"
value: ${{ jobs.deploy.outputs.deployment_url }}
preview_url:
description: "Permanent URL where the deployment can be accessed"
value: ${{ jobs.deploy.outputs.preview_url }}

jobs:
deploy:
Expand All @@ -69,6 +72,7 @@ jobs:
url: ${{ steps.deployment-url.outputs.url }}
outputs:
deployment_url: ${{ steps.deployment-url.outputs.url }}
preview_url: ${{ steps.preview-url.outputs.url }}
steps:
- uses: actions/[email protected]

Expand Down Expand Up @@ -196,16 +200,23 @@ jobs:
with:
bucket_name: ${{ inputs.bucket_name }}

- name: Get Preview Url
id: preview-url
run: |
preview_url="https://${{ steps.cursor-update.outputs.branch_label }}.${{ inputs.domain_name }}"
echo "Permanent preview url $preview_url"
echo "url=$preview_url" >> $GITHUB_OUTPUT

- name: Update PR Description
uses: pleo-io/spa-tools/actions/[email protected]
if: ${{ github.event_name == 'pull_request' && steps.is-version-already-deployed.outputs.is_deployed == 'false' }}
if: ${{ github.event_name == 'pull_request' && inputs.app_name && steps.is-version-already-deployed.outputs.is_deployed == 'false' }}
with:
app_name: ${{ inputs.app_name }}
app_icon: ${{ inputs.app_icon }}
as_labels: true
links: |
[
{"name": "${{ inputs.app_name }} preview", "url": "https://${{ steps.cursor-update.outputs.branch_label }}.${{ inputs.domain_name }}"},
{"name": "${{ inputs.app_name }} preview", "url": "${{ steps.preview-url.outputs.url }}"},
{"name": "Current Permalink", "url": "${{ steps.deployment-url.outputs.url }}"}
]

Expand Down
1 change: 1 addition & 0 deletions reusable-workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ jobs:
if: steps.s3-cache.outputs.processed == 'false'
run: ${{ inputs.build_cmd }}
env:
NOTION_API_KEY: ${{ secrets.NOTION_API_KEY }}
TURBO_TOKEN: ${{ inputs.turbo_cache && secrets.TURBO_TOKEN || null }}
TURBO_FORCE: ${{ inputs.turbo_force }}

Expand Down
15 changes: 13 additions & 2 deletions reusable-workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ on:
deployment_url:
description: "URL where the deployment can be accessed"
value: ${{ jobs.deploy.outputs.deployment_url }}
preview_url:
description: "Permanent URL where the deployment can be accessed"
value: ${{ jobs.deploy.outputs.preview_url }}

jobs:
deploy:
Expand All @@ -69,6 +72,7 @@ jobs:
url: ${{ steps.deployment-url.outputs.url }}
outputs:
deployment_url: ${{ steps.deployment-url.outputs.url }}
preview_url: ${{ steps.preview-url.outputs.url }}
steps:
- uses: actions/[email protected]

Expand Down Expand Up @@ -196,16 +200,23 @@ jobs:
with:
bucket_name: ${{ inputs.bucket_name }}

- name: Get Preview Url
id: preview-url
run: |
preview_url="https://${{ steps.cursor-update.outputs.branch_label }}.${{ inputs.domain_name }}"
echo "Permanent preview url $preview_url"
echo "url=$preview_url" >> $GITHUB_OUTPUT

- name: Update PR Description
uses: pleo-io/spa-tools/actions/[email protected]
if: ${{ github.event_name == 'pull_request' && steps.is-version-already-deployed.outputs.is_deployed == 'false' }}
if: ${{ github.event_name == 'pull_request' && inputs.app_name && steps.is-version-already-deployed.outputs.is_deployed == 'false' }}
with:
app_name: ${{ inputs.app_name }}
app_icon: ${{ inputs.app_icon }}
as_labels: true
links: |
[
{"name": "${{ inputs.app_name }} preview", "url": "https://${{ steps.cursor-update.outputs.branch_label }}.${{ inputs.domain_name }}"},
{"name": "${{ inputs.app_name }} preview", "url": "${{ steps.preview-url.outputs.url }}"},
{"name": "Current Permalink", "url": "${{ steps.deployment-url.outputs.url }}"}
]

Expand Down
1 change: 1 addition & 0 deletions terraform-module/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ No resources.
| <a name="input_default_repo_branch_name"></a> [default\_repo\_branch\_name](#input\_default\_repo\_branch\_name) | Name of the default branch of the project repo | `string` | `"master"` | no |
| <a name="input_env"></a> [env](#input\_env) | Environment (production/staging) | `string` | n/a | yes |
| <a name="input_is_robots_indexing_allowed"></a> [is\_robots\_indexing\_allowed](#input\_is\_robots\_indexing\_allowed) | Should allow search engine indexing in production? | `bool` | `true` | no |
| <a name="input_serve_nested_index_html"></a> [serve\_nested\_index\_html](#input\_serve\_nested\_index\_html) | Applies to apps which build separate index.html files for sub-routes, e.g. using Gatsby SSG | `bool` | n/a | yes |
| <a name="input_subdomain"></a> [subdomain](#input\_subdomain) | Subdomain where the app lives (e.g. 'hello' if the app lives at hello.example.com) | `string` | n/a | yes |
| <a name="input_zone_domain"></a> [zone\_domain](#input\_zone\_domain) | The domain where the app lives (e.g. 'example.com' if the app lives at hello.example.com) | `string` | n/a | yes |

Expand Down
311 changes: 295 additions & 16 deletions terraform-module/edge-lambdas/dist/viewer-request/index.js

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion terraform-module/edge-lambdas/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
"version": "1.4.1",
"name": "@pleo-io/spa-edge-lambdas",
"dependencies": {
"@aws-sdk/client-s3": "^3.329.0"
"@aws-sdk/client-s3": "^3.329.0",
"mime-types": "^2.1.35"
},
"devDependencies": {
"@swc/jest": "0.2.36",
"@types/aws-lambda": "8.10.126",
"@types/jest": "29.5.8",
"@types/mime-types": "^2.1.4",
"@types/node": "20.8.6",
"@vercel/ncc": "0.38.1",
"jest": "29.7.0",
Expand Down
25 changes: 25 additions & 0 deletions terraform-module/edge-lambdas/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions terraform-module/edge-lambdas/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ export type Config = {
originBucketRegion: string
previewDeploymentPostfix?: string
defaultBranchName?: string
serveNestedIndexHtml?: boolean
}
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,31 @@ describe(`Viewer request Lambda@Edge`, () => {
expect(request).toEqual(requestFromEvent(expectedEvent))
})

test(`Handles requests for nested routes serveNestedIndexHtml is true`, async () => {
const appVersion = getRandomSha()
const host = 'my-feature.app.staging.example.com'
const event = mockRequestEvent({host, uri: '/home/home.landing', appVersion})
mockedFetchFileFromS3Bucket.mockResolvedValue(appVersion)

const handler = getHandler(
{
...originConfig,
previewDeploymentPostfix: '.app.staging.example.com',
serveNestedIndexHtml: true
},
mockS3
)
const request = await handler(event, mockContext, mockCallback)

expectAppVersionFetched('deploys/my-feature')
const expectedEvent = mockRequestEvent({
host,
uri: `/html/${appVersion}/home/home.landing/index.html`,
appVersion
})
expect(request).toEqual(requestFromEvent(expectedEvent))
})

test(`Handles requests for well known files`, async () => {
const appVersion = getRandomSha()
const host = 'my-feature.app.staging.example.com'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as path from 'path'

import {CloudFrontRequest, CloudFrontRequestHandler} from 'aws-lambda'
import {S3Client} from '@aws-sdk/client-s3'
import mime from 'mime-types'

import {APP_VERSION_HEADER, getHeader, setHeader} from '../utils'
import {fetchFileFromS3Bucket} from '../s3'
Expand Down Expand Up @@ -40,7 +41,7 @@ export function getHandler(config: Config, s3: S3Client) {
request.headers = setHeader(request.headers, APP_VERSION_HEADER, appVersion)

// We instruct the CDN to return a file that corresponds to the app version calculated
const uri = getUri(request, appVersion)
const uri = getUri(request, appVersion, config.serveNestedIndexHtml)
request.uri = uri
} catch (error) {
console.error(error)
Expand All @@ -58,16 +59,26 @@ export function getHandler(config: Config, s3: S3Client) {
/**
* We respond with a requested file, but prefix it with the hash of the current active deployment
*/
function getUri(request: CloudFrontRequest, appVersion: string) {
// If the
// - request uri is for a specific file (e.g. "/iframe.html")
// - or is a request on one of the .well-known paths (like .well-known/apple-app-site-association)
// we serve the requested file.
// Otherwise, for requests uris like "/" or "my-page" we serve the top-level index.html file,
// which assumes the routing is handled client-side.
const isFileRequest = request.uri.split('/').pop().includes('.')
function getUri(request: CloudFrontRequest, appVersion: string, serveNestedIndexHtml: boolean) {
const isFileRequest = Boolean(mime.lookup(request.uri))
const isWellKnownRequest = request.uri.startsWith('/.well-known/')
const filePath = isFileRequest || isWellKnownRequest ? request.uri : '/index.html'
const filePath = (() => {
// If the
// - request uri is for a specific file (e.g. "/iframe.html")
// - or is a request on one of the .well-known paths (like .well-known/apple-app-site-association)
// we serve the requested file.
if (isFileRequest || isWellKnownRequest) {
return request.uri
}
// Otherwise, for requests uris like "/" or "/my-page" we
// check the serveNestedIndexHtml config, if
// - true, we append index.html to the original request URI
// - false, we serve the top-level index.html file, as all the routing is handled client-side.
if (serveNestedIndexHtml) {
return request.uri.replace(/\/$/, '') + '/index.html'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't we need to consider the json files generated by gatsby (or other SSGs) for navigation after initial load?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't those be handled by the previous condition isFileRequest?
Frankly I'm not loving that we are handling this ourselves this way but is seems to have served us well so far.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah you're right, didn't make a lot of sense what I wrote - this is only for rewriting paths where we don't have a file.

}
return '/index.html'
})()

return path.join('/html', appVersion, filePath)
}
Expand Down
1 change: 1 addition & 0 deletions terraform-module/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ module "lambdas" {
bucket_name = module.s3.bucket_name
bucket_region = module.s3.bucket_region
domain_name = local.domain_name
serve_nested_index_html = var.serve_nested_index_html

providers = {
aws.global = aws.global
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ resource "local_file" "lambda_config" {
"originBucketRegion" = var.bucket_region
"previewDeploymentPostfix" = var.env == "production" ? "" : ".${var.domain_name}"
"defaultBranchName" = var.default_repo_branch_name
"serveNestedIndexHtml" = var.serve_nested_index_html
})
filename = "${path.root}/dist/${var.app_name}/${var.event_type}/config.json"
}
Expand Down
5 changes: 5 additions & 0 deletions terraform-module/modules/frontend-spa-edge-lambda/vars.tf
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,8 @@ variable "domain_name" {
description = "App domain name"
type = string
}

variable "serve_nested_index_html" {
description = "Applies to apps which build separate index.html files for sub-routes, e.g. using Gatsby SSG"
type = bool
}
5 changes: 5 additions & 0 deletions terraform-module/vars.tf
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,8 @@ variable "is_robots_indexing_allowed" {
default = true
type = bool
}

variable "serve_nested_index_html" {
description = "Applies to apps which build separate index.html files for sub-routes, e.g. using Gatsby SSG"
type = bool
}
Loading