Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 147 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# Copilot Agent Instructions for firebase-tools-cli

## Conventional Commits

All commit messages should follow the [Conventional Commits](https://www.conventionalcommits.org/) specification where possible. Only basic format is checked by CI (non-empty message).

### Format

```
type(scope)!: subject
```

- **type** (optional): `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert` — or any descriptive word
- **scope** (optional): area of the codebase affected — see allowed scopes below
- **`!`** (optional): denotes a breaking change
- **subject** (optional): describe the change

### Examples

```
feat(cli): add --dry-run flag to firestore export
fix(rtdb): handle missing databaseURL gracefully
docs: update README with new auth commands
chore: bump version to 0.6.0
ci: add commitlint workflow
refactor(firestore)!: rename importData to importDocuments
```

### Allowed Scopes

`cli`, `firestore`, `rtdb`, `remote-config`, `auth`, `config`, `release`, `docs`, `deps`

---

## PR Title Convention

PR titles must follow the same Conventional Commits format as commit messages:

```
type(scope)!: subject
```

This is enforced by the **Conventional PR Title** CI workflow. A PR with a non-conforming title will fail the check.

### Valid PR title examples

```
feat(auth): add Google sign-in support
fix(rtdb): resolve connection timeout on large datasets
chore: bump version to 0.6.0
docs: clarify setup instructions in README
```

---

## Branch Naming Conventions

Follow the pattern `type/short-description` or `type/issue-short-description`:

```
feat/add-auth-login
fix/rtdb-timeout
docs/update-contributing
chore/bump-dependencies
ci/add-commitlint
```

---

## ⚠️ High-Risk Areas — Handle with Extra Care

### `dist/` Build Artifacts
- **Never commit `dist/`** to the repository. It is listed in `.gitignore`.
- The `dist/` folder is generated by the build and bundled by esbuild.
- CI will build from source; any manual edits to `dist/` will be lost.

### CLI Packaging
- The package is distributed as a CLI binary via npm (`firebase-tools-cli`).
- Entry point: `dist/index.js` (must be executable — `chmod +x` is applied in the build).
- The `bin` field in `package.json` must not be changed without updating the shebang and build config.
- Always verify `firebase-tools-cli --help` works after packaging.

### Node 18 Compatibility
- Minimum Node.js version is `18.0.0` (see `engines` in `package.json`).
- Do **not** use Node.js APIs introduced after v18 without a compatibility guard.
- CI runs on Node 18; your code must work on Node 18.

### Tests Not Configured
- `npm test` currently exits with an error ("no test specified").
- Do **not** rely on tests to validate correctness. Use manual CLI verification instead.
- If you add tests, update `package.json` scripts accordingly.

---

## Mandatory Verification Steps Before Opening a PR

Run every step below and confirm it passes before opening or updating a PR:

```bash
# 1. Install dependencies from lockfile
npm ci

# 2. TypeScript type check (no emit)
npx tsc --noEmit

# 3. Prettier formatting check
npx prettier --check "src/**/*.{ts,js,json}"

# 4. Build the project
npm run build

# 5. Package and test CLI installation end-to-end
npm pack
npm install -g firebase-tools-cli-*.tgz
firebase-tools-cli --help
```

All steps must exit with code 0 before the PR is opened.

---

## Base Branch Rule (MANDATORY)

When opening a pull request, always set the base branch as follows:

- **Default:** `base = development` — use this for all regular work (features, fixes, docs, ci, chore, etc.)
- **Parent branch:** if the work is a follow-up to an existing branch/PR, use that branch as the base (not `development` and not `main`)
- **`main`:** only use `base = main` if explicitly instructed by a maintainer (e.g., hotfix release)

> Never default to `main` unless a maintainer explicitly requests it.

---

## PR Checklist

Include this checklist in every PR description:

- [ ] PR title is non-empty (conventional format recommended but not required)
- [ ] All commit messages follow Conventional Commits format
- [ ] `npm ci` passes without errors
- [ ] `npx tsc --noEmit` passes
- [ ] `npx prettier --check "src/**/*.{ts,js,json}"` passes
- [ ] `npm run build` produces a working `dist/`
- [ ] `npm pack && npm install -g firebase-tools-cli-*.tgz && firebase-tools-cli --help` succeeds
- [ ] No `dist/` files are committed
- [ ] Documentation updated if user-facing functionality changed
- [ ] Relevant issue linked (non-issue PRs are not merged)
33 changes: 33 additions & 0 deletions .github/workflows/commitlint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Commitlint

on:
pull_request:
branches: [main, development]

permissions:
contents: read

jobs:
commitlint:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Use Node.js 18.x
uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Lint commit messages
run: |
npx commitlint \
--from ${{ github.event.pull_request.base.sha }} \
--to ${{ github.event.pull_request.head.sha }} \
--verbose
35 changes: 35 additions & 0 deletions .github/workflows/conventional-pr-title.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Conventional PR Title

on:
pull_request:
types: [opened, edited, reopened, synchronize]
branches: [main, development]

permissions:
contents: read

jobs:
validate-pr-title:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Validate PR title follows Conventional Commits
env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: |
echo "PR title: $PR_TITLE"

# Conventional Commits regex (lenient):
# type(scope)!: subject
# - type: any word (optional)
# - scope: optional, alphanumeric with hyphens
# - !: optional breaking change marker
# - subject: anything after the colon
PATTERN='^([a-zA-Z][a-zA-Z0-9_-]*(\([a-zA-Z0-9_-]+\))?!?: .*|.+)$'

if echo "$PR_TITLE" | grep -qP "$PATTERN"; then
echo "✅ PR title is valid."
else
echo "❌ PR title must not be empty."
exit 1
fi
69 changes: 57 additions & 12 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,39 +53,84 @@ Thank you for your interest in contributing to Firebase Tools CLI! This guide wi

### Before You Submit

1. **Test your changes** thoroughly
2. **Run the linter** to check for code style issues
3. **Make sure all tests pass**
4. **Update documentation** if necessary
1. **Run all checks** to ensure everything passes:

### Creating a Pull Request
```bash
npm ci
npx tsc --noEmit
npx prettier --check "src/**/*.{ts,js,json}"
npm run build
npm pack && npm install -g firebase-tools-cli-*.tgz && firebase-tools-cli --help
```

2. **Update documentation** if your changes affect user-facing functionality.

### Commit Message Convention

Commit messages should follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) where possible. CI checks that commit messages are non-empty; type and subject format are not strictly enforced.

**Recommended format:** `type(scope)!: subject`

| Field | Details |
|-------|---------|
| `type` | Optional: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert` |
| `scope` | Optional: `cli`, `firestore`, `rtdb`, `remote-config`, `auth`, `config`, `release`, `docs`, `deps` |
| `!` | Optional: marks a breaking change |
| `subject` | Describe the change (no format enforcement) |

1. **Conventional commits** for your commit messages. For details, see [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/).
**Examples:**

2. **Conventional branch names** for your branch names. For details, see [Conventional Branches](https://conventional-branch.github.io/). For example, `feat/add-new-feature` or `bugfix/fix-123`.
```
feat(cli): add --dry-run flag to firestore export
fix(rtdb): handle missing databaseURL gracefully
docs: update README with new auth commands
chore: bump version to 0.6.0
```

### PR Title Convention

PR titles should follow the Conventional Commits format where possible. The CI workflow only checks that the title is non-empty.

```
type(scope)!: subject
```

### Branch Naming Conventions

Follow the pattern `type/short-description`:

```
feat/add-auth-login
fix/rtdb-timeout
docs/update-contributing
chore/bump-dependencies
```

### Creating a Pull Request

3. **Push your branch** to your fork:
1. **Push your branch** to your fork:

```bash
git push origin feature/your-feature-name
git push origin feat/your-feature-name
```

4. **Create a pull request** on GitHub:
2. **Create a pull request** on GitHub:

- Go to the main repository
- Click "New Pull Request"
- Select your branch
- Fill out the pull request template

5. **Write a clear description** that includes:
3. **Write a clear description** that includes:
- What changes you made
- Why you made them
- Any relevant issue numbers (e.g., "Fixes #123")

### Pull Request Guidelines

- **One feature per PR**: Keep pull requests focused on a single feature or fix
- **Clear title**: Use a descriptive title that explains what the PR does
- **Conventional title**: PR title must follow the Conventional Commits format — enforced by CI
- **Conventional commits**: All commits must follow Conventional Commits — enforced by CI
- **Detailed description**: Explain the changes and why they're needed
- **Link issues**: Reference any related issues in your description. Non-issue PRs will not be merged.

Expand Down
15 changes: 15 additions & 0 deletions commitlint.config.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/** @type {import('@commitlint/types').UserConfig} */
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [0],
'type-case': [0],
'type-empty': [0],
'scope-enum': [0],
'scope-empty': [0],
'subject-case': [0],
'subject-empty': [0],
'subject-full-stop': [0],
'subject-max-length': [0],
},
};
Loading
Loading