diff --git a/.github/workflows/deposits_data_cronjob.yml b/.github/workflows/deposits_data_cronjob.yml new file mode 100644 index 0000000..0bf6b6d --- /dev/null +++ b/.github/workflows/deposits_data_cronjob.yml @@ -0,0 +1,73 @@ +name: Update deposits data + +on: + schedule: + # https://crontab.guru/#0_9_*_*_* + # Every day at 9am UTC + - cron: "0 9 * * *" + +jobs: + deploy: + name: Deployment + runs-on: ubuntu-latest + + steps: + # Ref: https://github.com/actions/checkout/issues/1471#issuecomment-1771231294 + - uses: actions/checkout@v4 + + - name: Get latest tag available + id: latest_tag + run: echo "tag=$(git ls-remote --tags --sort=committerdate | grep -o 'v.*' | sort -r | head -1)" >> "$GITHUB_OUTPUT" + + - name: Tag checkout + run: | + git fetch --prune --unshallow --tags + git checkout ${{ steps.latest_tag.outputs.tag }} + + - uses: actions/cache@v2 + with: + path: '**/node_modules' + key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }} + + - name: Setup Node.js + uses: actions/setup-node@v2 + + - name: Install + run: | + rm -rf .cache + rm -rf build + yarn config set cache-folder .yarn + yarn install + pip install awscli --upgrade --user + + - name: Build deposits map + run: yarn update-deposits + + - name: Configure AWS Production credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.PROD_AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.PROD_AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ secrets.PROD_AWS_DEFAULT_REGION }} + + # Script to deploy to release environment + - name: 'Deploy to S3: Release' + run: | + aws s3 sync data s3://${{ secrets.RELEASE_BUCKET_NAME }}/data --delete + + - name: 'Cloudfront: cache invalidation' + run: | + aws cloudfront create-invalidation --distribution-id ${{ secrets.PROD_AWS_CLOUDFRONT_ID }} --paths "/data/*" + + notify: + uses: ./.github/workflows/slack_release_notification.yml + if: ${{ always() }} + needs: [ deploy ] + secrets: + RELEASES_SLACK_WEBHOOK_URL: ${{ secrets.RELEASES_SLACK_WEBHOOK_URL }} + with: + environment: Production + service: GC Deposit UI - Update Deposits + success: ${{ contains(join(needs.*.result, ','), 'success') }} + message: "deploy service `GC Deposit UI - Update Deposits data` version `${{ inputs.tag }}`. Triggered by `${{ github.actor }}`." + \ No newline at end of file diff --git a/.github/workflows/dev_ci.yml b/.github/workflows/dev_deploy.yml similarity index 86% rename from .github/workflows/dev_ci.yml rename to .github/workflows/dev_deploy.yml index 2efe442..059eeeb 100644 --- a/.github/workflows/dev_ci.yml +++ b/.github/workflows/dev_deploy.yml @@ -54,14 +54,18 @@ jobs: - name: 'Deploy to S3: Development' if: github.ref == 'refs/heads/dev' run: | - aws s3 sync build/ s3://${{ secrets.DEV_BUCKET_NAME }}/dev --exclude "*.html" --cache-control max-age=0,no-cache,no-store,public - aws s3 sync build/ s3://${{ secrets.DEV_BUCKET_NAME }}/dev --exclude "*" --include "*.html" --cache-control max-age=0,no-cache,no-store,must-revalidate --content-type text/html + aws s3 sync build/ s3://${{ secrets.DEV_BUCKET_NAME }}/dev --exclude "*.html" --cache-control max-age=0,no-cache,no-store,public --delete + aws s3 sync build/ s3://${{ secrets.DEV_BUCKET_NAME }}/dev --exclude "*" --include "*.html" --cache-control max-age=0,no-cache,no-store,must-revalidate --content-type text/html --delete aws s3 sync data s3://${{ secrets.DEV_BUCKET_NAME }}/dev/data --delete # Script to deploy to staging environment - name: 'Deploy to S3: Staging' if: github.ref == 'refs/heads/main' run: | - aws s3 sync build/ s3://${{ secrets.DEV_BUCKET_NAME }}/main --exclude "*.html" --cache-control max-age=0,no-cache,no-store,public - aws s3 sync build/ s3://${{ secrets.DEV_BUCKET_NAME }}/main --exclude "*" --include "*.html" --cache-control max-age=0,no-cache,no-store,must-revalidate --content-type text/html - aws s3 sync data s3://${{ secrets.DEV_BUCKET_NAME }}/main/data --delete \ No newline at end of file + aws s3 sync build/ s3://${{ secrets.DEV_BUCKET_NAME }}/main --exclude "*.html" --cache-control max-age=0,no-cache,no-store,public --delete + aws s3 sync build/ s3://${{ secrets.DEV_BUCKET_NAME }}/main --exclude "*" --include "*.html" --cache-control max-age=0,no-cache,no-store,must-revalidate --content-type text/html --delete + aws s3 sync data s3://${{ secrets.DEV_BUCKET_NAME }}/main/data --delete + + - name: 'Cloudfront: cache invalidation' + run: | + aws cloudfront create-invalidation --distribution-id ${{ secrets.DEV_AWS_CLOUDFRONT_ID }} --paths "/*" \ No newline at end of file diff --git a/.github/workflows/prod_deploy.yml b/.github/workflows/prod_deploy.yml index 2a90bef..366d625 100644 --- a/.github/workflows/prod_deploy.yml +++ b/.github/workflows/prod_deploy.yml @@ -69,6 +69,10 @@ jobs: aws s3 sync build/ s3://${{ secrets.RELEASE_BUCKET_NAME }} --delete --exclude "*" --include "sitemap.xml" --cache-control max-age=0,no-cache,no-store,must-revalidate --content-type text/xml aws s3 sync data s3://${{ secrets.RELEASE_BUCKET_NAME }}/data --delete + - name: 'Cloudfront: cache invalidation' + run: | + aws cloudfront create-invalidation --distribution-id ${{ secrets.PROD_AWS_CLOUDFRONT_ID }} --paths "/*" + notify: uses: ./.github/workflows/slack_release_notification.yml if: ${{ always() }} diff --git a/components/validator.tsx b/components/validator.tsx index a8d247d..6eac330 100644 --- a/components/validator.tsx +++ b/components/validator.tsx @@ -13,44 +13,48 @@ export default function Validator() { const [errorMessage, setErrorMessage] = useState(""); const [loading, setLoading] = useState(false); const [step, setStep] = useState("validation"); - const onDrop = useCallback(async (acceptedFiles: File[], rejectedFiles: FileRejection[]) => { - if (rejectedFiles.length > 0) { - setErrorMessage("Please upload a valid JSON file."); - } else if (acceptedFiles.length > 0) { - const data = await Promise.all( - acceptedFiles.map( - (file) => - new Promise((resolve) => { - const reader = new FileReader(); - reader.onload = async (event) => { - if (event.target?.result) { - const depositData = JSON.parse(event.target.result as string); - resolve({ - fileName: file.name, - data: depositData, - }); - } - }; - reader.readAsText(file); - }) - ) - ); - try { - setLoading(true); - await validateStatus(data); - setStep("validated"); - setLoading(false); - } catch (error: unknown) { - console.log(error); - setLoading(false); - if (error instanceof Error) { - setErrorMessage(error.message); - } else { - setErrorMessage("An unexpected error occurred."); + + const onDrop = useCallback( + async (acceptedFiles: File[], rejectedFiles: FileRejection[]) => { + if (rejectedFiles.length > 0) { + setErrorMessage("Please upload a valid JSON file."); + } else if (acceptedFiles.length > 0) { + const data = await Promise.all( + acceptedFiles.map( + (file) => + new Promise((resolve) => { + const reader = new FileReader(); + reader.onload = async (event) => { + if (event.target?.result) { + const depositData = JSON.parse(event.target.result as string); + resolve({ + fileName: file.name, + data: depositData, + }); + } + }; + reader.readAsText(file); + }) + ) + ); + try { + setLoading(true); + await validateStatus(data); + setStep("validated"); + setLoading(false); + } catch (error: unknown) { + console.log(error); + setLoading(false); + if (error instanceof Error) { + setErrorMessage(error.message); + } else { + setErrorMessage("An unexpected error occurred."); + } } } - } - }, [validateStatus]); + }, + [validateStatus] + ); const { getRootProps, getInputProps } = useDropzone({ onDrop, accept: { "application/json": [] } }); return ( @@ -72,7 +76,7 @@ export default function Validator() { {errorMessage &&

{errorMessage.substring(0, 150)}

} ) : step === "validated" ? ( -
+
{statuses && statuses.length > 0 ? (
{statuses.map((status, index) => (