Resolvendo "GSutil cp error - CommandException No URLs matched"

Aventuras no front end. Subindo páginas "dinamicamente estaticas"

TL;DR

Just use:

.sh

gsutil rsync -d -m gs://your-path -r

Contexto

Antes de mostrar o problema e a solução (e as não soluções, porém pensadas), quero dar uma explicação de como cheguei nesse problema!

No meu atual projeto na empresa, nós usamos (e muito) o framework Next.JS, no qual temos alguns padrões de aplicações e uma delas que estamos construindo, é um site que não depende de SEO, logo, fizemos algo Client Side Rendering utilizando o Static Export.

Porém, precisamos que o acesso a urls dinâmicas e com dados funcione, mesmo sendo algo 100% client side. E para que essa arquitetura funcione, ao criar uma rota dinâmica no Next.JS e rodar o export dessas rotas "estáticas", nossos htmls finais ficarão assim:

  • Exemplo de rota: /blog/:id:

.sh

|--/out
  |--/blog
    |--[id].html

E é ai que esbarramos no problema.

Deploy

Atualmente utilizamos os buckets da Google Cloud GCS como armazenamento para nossos arquivos estáticos e, consequentemente, usamos a CLI gsutil.

Inicialmente sempre utilizamos o comando de gsutil cp:

.sh

gsutil cp -r gs://path

Porém, como mostrei anteriormente, nosso padrão de arquivos buildados tem um caracter especial: [.

E ao rodar o comando de cp, recebemos o seguinte erro:

.sh

CommandException: No URLs matched: .ravejs/temp/out/[path0]

Esse erro acontece pois o gsutil não consegue entender que o padrão de pasta começando com [ é o nome "crú" do arquivo e não um wildcard.

Como esse padrão não pode ser mudado (url com paths dinâmicos com “[“), precisamos mudar o modo de fazer deploy, pois existe essa issue aqui: https://github.com/GoogleCloudPlatform/gsutil/issues/290 - de 2015 no gsutil cp, impossibilitando o uso padrão dele para nosso caso.

Possiveis Soluções

Pelo que vi, o gsutil cp deveria tratar essas urls como /out/blog/\[id\].html com um scape antes do ”[“.

Ou tratar dessa forma, colocando os brackets [], seguido de outros brackets:

.sh

gsutil cp -r gs://out/blog/[[]id[]].html

Patching gsutil

Aqui temos uma sugestão de aplicar um patch/fork no gsutil e ignorar essa regra de brackets wildcards, porém ficando em uma versão totalmente disconexa com a ferramenta, assim disconsiderei totalmente,

https://stackoverflow.com/questions/36780004/gsutil-cant-touch-a-file-with-brackets-in-the-name

Script customizado para cada arquivo!

Outra ideia que testei, foi criar um script para rodar com node.js, Onde lê as páginas que contém path dinâmico e tenta subir com o gsutil cp alterando o “padrão glob” para algo que ele entenda:

.js

import { exec, execSync } from "child_process";
import fs from "fs";
const cwd = process.cwd();
 
const manifest = JSON.parse(
  fs.readFileSync(`${cwd}/.next/build-manifest.json`, "utf-8"),
);
const pagesWithDynamicPath = Object.keys(manifest.pages).filter((pagePath) => {
  return /\[*\]/.test(pagePath);
});
 
pagesWithDynamicPath.forEach((page) => {
  const parsedPathRegex = page // or try to parse [] with a scape "\[\]" before each [
    .concat("/")
    .replace(/\[/g, "[[]")
    .replace(/\]\//g, "[]]/")
    .replace(/\/$/, "");
 
  execSync(
    `gsutil cp -r 'gs://${cwd}/out${parsedPathRegex}'`
  );
});

Porém, mesmo assim o comando continuou dando o problema de que não encontrava o arquivo.

Solução final: usar rsync

Baseado nesse comentário, dessa issue, dá para ver que o rsync não faz esse “check” de wildcards mencionado na doc do google, então ao utilizar ele, funcionou.