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

Not all listed assets_manifests are backed up on deploy #264

Open
tenkiller opened this issue Sep 26, 2024 · 9 comments
Open

Not all listed assets_manifests are backed up on deploy #264

tenkiller opened this issue Sep 26, 2024 · 9 comments
Labels

Comments

@tenkiller
Copy link

Steps to reproduce

The following paths are added to my assets_manifest in deploy.rb:

  desc "define assets manifests files"
  task :set_assets_manifests do
    on roles(:app) do
      within release_path do
        with rails_env: fetch(:rails_env) do
          set :assets_manifests, [
            release_path.join("public", "vite", ".vite", "manifest*.*"),
            release_path.join("public", fetch(:assets_prefix), "manifest*.*"),
            release_path.join("public", fetch(:assets_prefix), ".sprockets-manifest*")
          ]
        end
      end
    end
  end

Listing the directory contents of these paths on the server yields:

$ ls -a public/vite/.vite/manifest*.*
public/vite/.vite/manifest-assets.json
public/vite/.vite/manifest-assets.json.gz
public/vite/.vite/manifest.json
public/vite/.vite/manifest.json.gz

$ ls -a public/assets/manifest*.*
public/assets/manifest-04024382391bb910584145d8113cf35ef376b55d125bb4516cebeb14ce788597.js
public/assets/manifest.js
public/assets/manifest-04024382391bb910584145d8113cf35ef376b55d125bb4516cebeb14ce788597.js.gz
public/assets/manifest.js.gz

$ ls -a public/assets/.sprockets-manifest*
public/assets/.sprockets-manifest-415461b893c1f289238ea242950f61cb.json

Expected behavior

I expect the files in the listed directories to appear in the assets_manifest_backup directory after deployment.

Actual behavior

What happens instead is the files that only match the first path in assets_manifests are backed up.

$ ls -a assets_manifest_backup/
.  
..  
manifest-assets.json  
manifest-assets.json.gz  
manifest.json  
manifest.json.gz

Is the return on L111 of assets.rake the culprit, perhaps?

fetch(:assets_manifests).each do |candidate|
  return capture(:ls, candidate).strip.gsub(/(\r|\n)/, ' ') if test(:ls, candidate)
end

System configuration

Ruby 3.2.2 (2023-03-30 revision e51014f9c0) [x86_64-darwin20]
Rubygems 3.5.11
Rails 7.1.4
Bundler 2.4.5

@mattbrictson
Copy link
Member

Thanks for the report! I'll look into it later this week.

@mattbrictson
Copy link
Member

Does the sprockets manifest get properly backed up if sprockets is the only value listed? I.e.

set :assets_manifests, [
  release_path.join("public", fetch(:assets_prefix), ".sprockets-manifest*")
]

@mattbrictson
Copy link
Member

Also what is your value of :assets_prefix? I assume it is assets, but just want to make sure.

@tenkiller
Copy link
Author

Yes, we're using the default prefix assets. And, correct, the sprockets manifest does get backed up when it is the first, or only, value listed.

$ ls -a assets_manifest_backup/
.  ..  .sprockets-manifest-415461b893c1f289238ea242950f61cb.json

@mattbrictson
Copy link
Member

As you've pointed out, although assets_manifests is an array, only the first value that matches a file path is actually used. I don't remember why it was implemented this way (despite the fact that I reviewed the PR, but that was 7 years ago 😄). Without completely understanding it, I am hesitant to change this behavior because existing users of capistrano may rely on this quirk.

It looks like the values of assets_manifests are passed directly to ls. To solve this specific use case, where you want multiple manifests to be backed up (e.g. sprockets and vite), I wonder if you could take advantage of the ls behavior and list all the glob patterns as a single string value.

In other words:

set :assets_manifests, [
  [
    release_path.join("public", "vite", ".vite", "manifest*.*"),
    release_path.join("public", fetch(:assets_prefix), "manifest*.*"),
    release_path.join("public", fetch(:assets_prefix), ".sprockets-manifest*")
  ].join(" ")
]

This will produce an array of length 1 that contains the various globs separated by spaces. When passed to ls, it should find all files that match any of those globs.

If this works, maybe we can explain this in the README.

@tenkiller
Copy link
Author

tenkiller commented Oct 3, 2024

Good insight @mattbrictson. That did the trick. After joining the assets manifests paths into a single string, fed into ls, this is the result of my assest_manifest_backup/ directory.

$ ls -a assets_manifest_backup/ | sort
.
..
manifest-04024382391bb910584145d8113cf35ef376b55d125bb4516cebeb14ce788597.js
manifest-04024382391bb910584145d8113cf35ef376b55d125bb4516cebeb14ce788597.js.gz
manifest-assets.json
manifest-assets.json.gz
manifest.js
manifest.js.gz
manifest.json
manifest.json.gz
.sprockets-manifest-415461b893c1f289238ea242950f61cb.json

The only hiccup I can foresee, is how would a rollback work? There's no path information for any of these files. So, if they are restored in a rollback, would they end up in the wrong directory from where they originated?

My scenario is most likely a special case where I have to support two frontend build workflows. Ideally, there'd only be one.

@mattbrictson
Copy link
Member

The closer I look at this, the more it seems that that backup/restore simply does not work. It completely fails to do the right thing when filenames change, as is the case with manifests that contain unique fingerprints in their names that potentially change in each release.

sources = targets.map do |target|
release_path.join('assets_manifest_backup', File.basename(target))
end

And when multiple asset pipelines are in use, if they generate the same manifest filename (but store them in different directories) their backups/restores will clobber each other, because the backups directory is completely flat (no sub-directories).

There was probably a time where there was ever only 1 manifest, and that manifest did not use a fingerprinted file name, but with modern asset pipelines this is clearly no longer the case.

Unless I am misreading the code, I think the assets_manifests feature is effectively broken when used with modern Rails and will need to be rebuilt.

@tenkiller
Copy link
Author

@mattbrictson Thanks for looking into this. I appreciate the timely responses. Technically, it's not a blocker for us. We don't actually perform rollbacks in production for numerous reasons. I was just curious why it didn't work the way I anticipated.

@mattbrictson
Copy link
Member

I, too, do not use rollbacks in production. This may explain why this area of the code has not gotten much scrutiny over the years. 😅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants