Open plamber opened 3 years ago
Hey @plamber, this is a really interesting suggestion and definitely an improvement to the service. Let me sync with the team and decide on a path forward for enabling this!
Thank you @miwebst. Such capability would simplify our deployment strategy a lot.
Today we solved it by having a blob storage where we are pushing the files we are building. Afterwards we download the whole folder to our pipeline and push the contents to the Azure Static Web App.
The solution works but has a lot of moving parts that might break.
So upon thinking about this further, I think there are 2 layers to this:
Is it correct to say that 2 is the issue that is affecting your app deployment? I think I was thinking 1 upon initial reading but after re-reading it seems more likely that you'd like for these old files to hang around for old clients.
Hi @miwebst,
Is not directly affecting our scenario. The only drawback I see is increase of network bandwidth if you are deploying many times.
Changing the ETAGs is certainly a good idea for Frameworks that require a cache invalidation and do not handle it by their own like in React. In the classic React setup you have a sort of "guaranteed" invalidation based on filenames and the naming convention. The index.html will reference to the newly generated files and also be different compared to the previous index.html if something changed.
Affecting our deployment.
By removing the "no longer needed" files you are breaking all applications referencing to old CSS and JS files. Keeping the existing files in the directory and just overwriting what has been changed would solve all our issues.
The user has still an old version of the index.html (maybe for a couple of minutes, hours, days). The code will reference the old files until the index.html is being updated.
The application will run without issues because it is referencing the new files.
Does it make sense for you?
Cheers
@miwebst
Something came into my mind that might be a nice addition to the issue 2.
When keeping the old files and uploading new ones you might come to the situation of having a lot of "garbage" files lying on your Static Web App. You could also solve this by deleting files that are older than x-days from the storage.
Let us imagine I am doing continuous deployments every day. This might add few changing files every day. The clients will certainly update their cache after a couple of days or so. There is no reason of keeping the "old" files on the storage. Having the option to tell you, upload these files and delete all files with a modify date older than x-days could solve this problem.
Yes I think this ask makes sense. We will need to think more about supporting this scenario. Just curious, what would be your expectation for allowing old clients to refresh? 1-day, 7-days, 30-days?
Hi, Would rather go to 30, 60, or oven 90d. Maybe keeping it 30d default with the possibility to pass you a numeric value in the configuration. Maybe also passing -1 meaning that it should not delete the files at all.
I am thinking of users that are not reguraly visiting the site. Having just 7d is not fixing the problem.
Cheers
@anthonychu may be able to provide some guidance on available options here.
I think this is better solved by using built-in capabilities in frameworks like error boundaries in React to catch the exceptions when the bundles have disappeared and act accordingly, such as reloading the app.
This should only be an issue when an app is open and you navigate to a route that attempts to download a bundle that's already gone. /index.html shouldn't have a long cache expiry, so folks returning to an app should get an updated version.
Hi @anthonychu, after all the issues we encountered in the Teams client we implemented an error boundary as suggested in this post.
Do you have some other suggestions how to ensure that this becomes failsafe?
Thank you for your feedback, Patrick
We are considering using a static web app to host some html reports from tools like JMeter and Cucumber so that we can securely and conveniently share the output of build pipelines with testers.
It would be really helpful to be able to upload new static content without removing the existing content.
Otherwise each pipeline has to download all the other reports that should still be on the site and add its own before uploading. This could have concurrency problems if several pipelines complete at the same time.
@tribalsarthur Can you use a separate branch/repo for the static web app? Your pipelines can use git to pull, add the files, and push to deploy new test results.
I think this is better solved by using built-in capabilities in frameworks like error boundaries in React to catch the exceptions when the bundles have disappeared and act accordingly, such as reloading the app.
This should only be an issue when an app is open and you navigate to a route that attempts to download a bundle that's already gone. /index.html shouldn't have a long cache expiry, so folks returning to an app should get an updated version.
This is not a great solution for single-page apps, where the /index.html page might be in use for a long period of time. I'd have expected that 'previous' static files would remain in the edge node cache for a configurable amount of time, where they're not included in a subsequent deployment.
I think we're going to have to get around this by putting Front Door in front of the ASWA, in which case the files might as well have been hosted in a regular Storage Account.
@andyblack19 and @anthonychu
We solved our problem by uploading all incremental files to a storage account to keep track of the old assets. The contents of this storage account are then pushed to the Static Web App.
This is a workaround but at least we won't have any file missing on our client applications.
@miwebst, can I please clarify what you mean by this?
On content update, we treat the update as atomic
Does this mean the upload process itself is atomic, i.e. index.html
will not become visible to clients before a JS file it references? Otherwise there is a potential problem where the sequence of events is:
index.html
is uploadedindex.html
<-- error occurs herefoo.js
is uploadedWe are currently trying to fix this exact issue with similar approach to @plamber using blob storage.
However this is really ineffecient and i feel like its begging for things to break. Having frameworks handle this really does not work for us since we have a lot of frequent deployments users can loose some data by reloading page. It would be really really cool if we can have the old files hang out ther for some time just a few days would work for us.
@plamber Perhaps you can help.
I'm running into thiss issue when i switched to the deployment strategy as you mentioned using azure blob storage, the config file staticwebapp.config.json file is no longer respected and i have the issue where the root url works but when trying to access some route the browser tries to access the html which does not exist and it fails with a 404 SO question here
Hi @Supcar27, we are still using static web apps but we keep a storage account to download, update, and then re-publish the contents to the static web app.
I can share with you the GitHub action we run. Hope this helps.
name: build and deploy
on:
workflow_call:
inputs:
environment:
description: The Github Environment to run
required: true
type: string
app-name:
description: The app name of the static web app
required: true
type: string
app-location:
description: The project location of the app
required: true
type: string
app-build-command:
description: The build command for this app
required: true
type: string
azure-storage-account:
description: Storage account to store the temporary files
required: true
type: string
azure-storage-folder:
description: Storage account folder to store the temporary files
required: true
type: string
app-build-target:
description: defines the location of the app
default: "/dist"
type: string
secrets:
npm-token:
description: The npm token to restore the packages
required: true
env:
CI: false
permissions:
id-token: write
contents: read
jobs:
build-and-deploy:
runs-on: 'ubuntu-latest'
timeout-minutes: 15
environment:
name: ${{ inputs.environment }}
steps:
- name: Checkout repository
uses: actions/checkout@v3
- uses: bahmutov/npm-install@v1
with:
working-directory: ${{ inputs.app-location }}
env:
GITHUB_PACKAGE_TOKEN: ${{ secrets.npm-token }}
- name: Build
run: ${{ inputs.app-build-command }}
working-directory: ${{ inputs.app-location }}
- uses: azure/login@v1
with:
client-id: ${{ vars.AADAPPID }}
tenant-id: ${{ vars.TENANTID }}
subscription-id: ${{ vars.SUBSCRIPTIONID }}
- name: upload current build for solution merging
run: |
az storage blob upload-batch --overwrite --account-name ${{ inputs.azure-storage-account }} --source "${{ inputs.app-location }}${{ inputs.app-build-target }}/" --destination ${{ inputs.azure-storage-folder }}
- name: create target download folder for solution merging
shell: pwsh
run: |
New-Item "${{ inputs.app-location }}/build_final/" -ItemType Directory
- name: download merged solution before upload
run: |
az storage blob download-batch --account-name ${{ inputs.azure-storage-account }} --destination "${{ inputs.app-location }}/build_final/" --pattern "*.*" --source ${{ inputs.azure-storage-folder }}
- uses: azure/CLI@v1
with:
inlineScript: |
SWA_DEPLOYMENT_TOKEN=$(az staticwebapp secrets list -n ${{ inputs.app-name }} --query 'properties.apiKey' -o tsv)
echo "::add-mask::$SWA_DEPLOYMENT_TOKEN"
echo SWA_DEPLOYMENT_TOKEN=$SWA_DEPLOYMENT_TOKEN >> $GITHUB_ENV
- name: Deploy (SPA)
uses: Azure/static-web-apps-deploy@v1
with:
azure_static_web_apps_api_token: ${{ env.SWA_DEPLOYMENT_TOKEN }}
action: "upload"
app_location: "${{ inputs.app-location }}/build_final/."
skip_app_build: "true"
- name: deletes files on blob storage that are older than 3 months
shell: pwsh
run: |
$time = (Get-date).AddMonths(-3).ToString("yyyy-MM-ddT00:00Z")
az storage blob delete-batch --account-name ${{ inputs.azure-storage-account }} -s ${{ inputs.azure-storage-folder }} --if-unmodified-since $time
Cheers
Hello, we are using the Azure Static Web Apps in combination with React Apps. Like most of the React Applications we are using code splitting to bundle our resources.
The default webpack configuration in Create React App (and best practice) is to hash your assets.
Each of your JS and CSS files will have a unique hash appended to the filename that is generated, which allows you to use aggressive caching techniques to avoid the browser re-downloading your assets if the file contents haven’t changed.
If I am not mistaken when publishing a new version to the Azure Static Web app old deployed assets are being replaced by the new assets bringing up these new issue.
I would like to ask you what your recommendations and best practices are to tackle this problem? Do you think it might be possible to have an option to "incrementally" upgrade an app during deployment? My thought was to keep a copy of the current version residing on the Azure Web App and "just" override the files that have been changed, keeping the old files and not deleting them from the folder. There shouldn't be a big overhead over time. On the other hand, it will ensure that users are not facing such issues due to caching.
Alternatively, we would also be happy to have the opportunity to directly access the filesystem of the Static Web App and handle these deployment scenarios by our own.
Thank you very much for your feedback, Patrick