Skip to content

Add a mechanism to allow files to be uploaded in a specific order #44

Description

@cowboy

So I was using FTP-Deploy-Action and ran into an issue, but looking at the code, it seemed more relevant to file the issue in this repo - LMK if you want me to move it. Let me explain what I'm doing.

The website I'm deploying has:

  • an assets directory containing fingerprinted css and js files
  • an index.html file that loads fingerprinted assets and runs the app
  • a build.json file that is generated at build time with the commit sha, which the app polls for to see if it has changed, at which time it reloads the app

eg.

build/client/
|-- assets
|   |-- api-CxujkEL4.js
|   |-- build-info-uX_ocp1e.js
|   |-- chunk-KNED5TY2-B2LzAy-9.js
|   |-- (etc)
|   |-- test-nav-Df4njhOQ.css
|   |-- test-nav-QvsV6Ysp.js
|   `-- with-props-ColFkw8L.js
|-- build.json
`-- index.html

The problem Im running into is that I have no way to specify the order in which these files are uploaded. However, the files must be uploaded in a very specific order:

  1. All new fingerprinted asset files must first be uploaded. If a user loads the app, or the upload fails during this step, the app will continue to work as before, because nothing has changed with any existing files.
  2. The index.html file may now be uploaded. Once this file is uploaded, any user loading the app will see the new version.
  3. The build.json file may now be uploaded. Once this file is uploaded, the app will see the change and reload itself.
  4. No-longer-used files may now be deleted from the server.

If step 2 happens before the asset files are uploaded, any user loading the app would run into problems, because necessary asset files don't exist on the server yet.

If step 3 happens before index.html is uploaded, the app would reload itself prematurely and see no changes, because index.html wouldn't be on the server yet, so it would reload the old version. Once index.html was uploaded, the app would have no way of knowing it needed to re-reload itself.

Right now, I'm working around this limitation like so, but as you can imagine, this isn't ideal, as it adds complexity and makes the entire deploy process take significantly longer.

name: Deploy Web Dev

on:
  push:
    branches: [main]

jobs:
  deploy-dev:
    runs-on: ubuntu-latest
    concurrency:
      group: deploy-dev
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version-file: 'package.json'
      - run: npm ci
      - run: npm run build-ci

      # step 1: Upload fingerprinted assets, use a dummy state file so it doesn't delete anything.
      #         Unfortunately, this will re-upload unchanged files.

      - name: ftp deploy - step 1 - fingerprinted assets
        uses: SamKirkland/FTP-Deploy-Action@v4.3.5
        with:
          local-dir: ./build/client/
          username: ci
          password: ${{ secrets.FTP_PASSWORD }}
          server: ${{ secrets.FTP_SERVER }}
          state-name: ftp-deploy-sync-state-dummy-1.json
          exclude: |
            index.html
            build.json

      # step 2: Upload index.html which effectively makes the new version go live, still don't delete anything.

      - run: rm ./build/client/ftp-deploy-sync-state-dummy-1.json
      - name: ftp deploy - step 2 - index.html
        uses: SamKirkland/FTP-Deploy-Action@v4.3.5
        with:
          local-dir: ./build/client/
          username: ci
          password: ${{ secrets.FTP_PASSWORD }}
          server: ${{ secrets.FTP_SERVER }}
          state-name: ftp-deploy-sync-state-dummy-2.json
          exclude: |
            assets/**
            build.json

      # step 3: Upload build.json so that app can auto-update, overwrite dummy state files, delete as usual.
      #         Also, force-upload new dummy state files so no files will be deleted in steps 1+2 next time.

      - run: echo "cachebust-${{ github.run_id }}" > ./build/client/ftp-deploy-sync-state-dummy-1.json
      - run: echo "cachebust-${{ github.run_id }}" > ./build/client/ftp-deploy-sync-state-dummy-2.json
      - name: ftp deploy - step 3 - build.json
        uses: SamKirkland/FTP-Deploy-Action@v4.3.5
        with:
          local-dir: ./build/client/
          username: ci
          password: ${{ secrets.FTP_PASSWORD }}
          server: ${{ secrets.FTP_SERVER }}

So, here's the ask. Can an option be added to this library and to FTP-Deploy-Action that lets the user specify the order in which files should be uploaded?

It could possibly just be an array of glob patterns (like how exclude works). The glob patterns would be iterated over in order, and for each one, all not-already-added matching files would be added to a sorted output array.

The underlying code would be something along these lines:

function sortFiles(files, patterns) {
  const map = {};
  const sortedFiles = [];

  patterns.forEach((pattern) => {
    const files = multimatch(unsortedFiles, pattern);
    for (const file of files) {
      if (!map[file]) {
        map[file] = true;
        sortedFiles.push(file);
      }
    }
  });

  return sortedFiles;
}

Eg.

const unsortedFiles = [
  "build.json",
  "index.html",
  "assets/home-C6JigMfh.js",
  "assets/chunk-KNED5TY2-B2LzAy-9.js",
  "assets/build-info-uX_ocp1e.js",
  "assets/overlay-B98S4KzY.css",
  "assets/test-Dfnwt5EG.css",
  "assets/entry.client-Jga_eV3f.js",
];

const sortPatterns = ["assets/**", "index.html", "build.json"];
const sortedFiles = sortFiles(unsortedFiles, sortPatterns);

console.log(sortedFiles);
/*
[
  "assets/home-C6JigMfh.js",
  "assets/chunk-KNED5TY2-B2LzAy-9.js",
  "assets/build-info-uX_ocp1e.js",
  "assets/overlay-B98S4KzY.css",
  "assets/test-Dfnwt5EG.css",
  "assets/entry.client-Jga_eV3f.js",
  "index.html",
  "build.json",
]
*/

Which would allow me (or anyone else deploying a static generated site with fingerprinted assets) to do this:

      - name: ftp deploy
        uses: SamKirkland/FTP-Deploy-Action@v4.3.5
        with:
          local-dir: ./build/client/
          username: ci
          password: ${{ secrets.FTP_PASSWORD }}
          server: ${{ secrets.FTP_SERVER }}
          order: |
            assets/**
            index.html
            build.json

Thanks!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions