Skip to content

Apply parent .gitignore files when a Cloud Run source deploy zips a subfolder - #11400

Open
falahat wants to merge 1 commit into
bapi_local_build_monorepofrom
bapi_run_parent_gitignore
Open

falahat wants to merge 1 commit into
bapi_local_build_monorepofrom
bapi_run_parent_gitignore

Conversation

@falahat

@falahat falahat commented Oct 30, 2026

Copy link
Copy Markdown
Contributor

Description

Stacked on #11396. A Cloud Run source deploy of an app in a subfolder (e.g. firebase.json + functions/ + web/) zips only that subfolder, and only read .gitignore files inside it. Files ignored by a .gitignore higher up, like the project's top-level one, were uploaded, built, and could be served. App Hosting source deploys and local builds already exclude them.

The zip now also applies the .gitignore files from the project's root down to the folder it zips, like git. readdirRecursive gets a gitIgnoreRoot option for this, and createSourceDeployArchive takes the project's directory in place of its unused targetSubDir parameter.

Scenarios Tested

  • Unit tests: a parent .gitignore, a deeper .gitignore negation that overrides it, and a folder outside the root.
  • Deployed an app in web/ with a web/public/*.pem file that only the top-level .gitignore ignores:
    • Before: the Cloud Run source deploy served the file publicly.
    • After: 404, like App Hosting and local builds. The uploaded files now match App Hosting's exactly, and every route matches the Cloud Run local build.

Sample Commands

# With "rootDir": "web" on the service in firebase.json:
firebase deploy --only run:web

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds support for applying parent .gitignore files when archiving directories for deployment (e.g., in App Hosting and Cloud Run). It introduces a gitIgnoreRoot option to fsAsync.readdirRecursive to traverse and stack .gitignore rules from parent directories up to the specified root. Feedback on the changes suggests wrapping the statSync check in parentGitIgnores with a try-catch block to prevent potential filesystem permission errors from crashing the directory traversal.

Comment thread src/fsAsync.ts
Comment on lines +72 to +74
if (!existsSync(gitIgnorePath) || !statSync(gitIgnorePath).isFile()) {
continue;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Calling statSync(gitIgnorePath) directly can throw an error if there are permission issues (EACCES) or other filesystem restrictions on any of the parent directories, which would crash the entire directory traversal. Wrapping this check in a try-catch block ensures that any such filesystem errors are caught gracefully and logged as debug messages, allowing the traversal to continue safely.

    try {
      if (!existsSync(gitIgnorePath) || !statSync(gitIgnorePath).isFile()) {
        continue;
      }
    } catch (e: unknown) {
      logger.debug(`Error checking .gitignore file at ${gitIgnorePath}:`, e);
      continue;
    }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we can not load the .gitignore for a project we should fail the deployment/build because it's unsafe to proceed. We might accidentally upload files the user doesnt want.

@falahat
falahat force-pushed the bapi_run_parent_gitignore branch from 04af65a to 2d3c147 Compare October 30, 2026 21:49
@falahat
falahat added this pull request to stack #11404 October 30, 2026 22:06
…ubfolder

A Cloud Run source deploy of an app in a subfolder (e.g. firebase.json + functions/ + web/) zips
only that subfolder, and the zip only read .gitignore files inside it. Files ignored by a
.gitignore higher up, like the project's top-level one, were uploaded, built, and could be
served. App Hosting's source deploys and local builds walk from the project's root, so they
already excluded them.

readdirRecursive gets a gitIgnoreRoot option that also applies the .gitignore files from that
directory down to the one being walked, like git does. createSourceDeployArchive passes the
project's directory through it, in place of its unused targetSubDir parameter.

A .gitignore that can't be read now fails the walk instead of being skipped, since skipping it
could upload files that the user meant to leave out.
@falahat
falahat force-pushed the bapi_run_parent_gitignore branch from 2d3c147 to a32dfdf Compare November 2, 2026 00:40

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant