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
33 changes: 33 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: CI Pipeline

on:
push:
branches:
- main
pull_request:
branches:
- main

jobs:
build-and-test:
runs-on: ubuntu-latest
permissions:
contents: read

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Install dependencies
run: npm ci

- name: Check formatting and lint
run: npm run check

- name: Type check
run: npm run typecheck
8 changes: 4 additions & 4 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
name: "CodeQL"
name: 'CodeQL'

on:
push:
branches: [ main ]
branches: [main]
pull_request:
branches: [ main ]
branches: [main]

jobs:
analyze:
Expand All @@ -18,7 +18,7 @@ jobs:
strategy:
fail-fast: false
matrix:
language: ["javascript"]
language: ['javascript']

steps:
- name: Checkout repository
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/nodejs-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: Node.js Tests

on:
push:
branches: [ main ]
branches: [main]
pull_request:
branches: [ main ]
branches: [main]

jobs:
node-tests:
Expand Down
1 change: 1 addition & 0 deletions .husky/commit-msg
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
npx --no -- commitlint --edit $1
4 changes: 4 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh
set -e
npx lint-staged
npm run typecheck
6 changes: 6 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: check-merge-conflict
- id: check-yaml
6 changes: 6 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"singleQuote": true,
"printWidth": 80,
"trailingComma": "es5",
"semi": false
}
25 changes: 18 additions & 7 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,24 @@ This file follows the AGENTS.md conventions (see https://agents.md/) and provide
---

## Quick setup

- Install dependencies (root):
- `npm install`
- Run the test suite locally:
- `npm test`
- Run a specific package/tests (replace `<agent-folder>` with the folder path):
- `npx jest "<agent-folder>/tests"`
- Run tests with coverage report:
- `npm test -- --coverage`
- Verify coverage meets thresholds:
- `node scripts/check-coverage.js`
- npx jest "<agent-folder>/tests"

---

## Why AGENTS.md?

AGENTS.md is for precise, agent-focused instructions that complement README files. Use it to document build steps, dev commands, test steps, and any non-obvious processes an automated tool should know.

---

## Agent operation guidance (canonical guidance adapted)

- Prefer interactive or dev commands when iterating (e.g., `npm run dev`) and avoid running production-only commands (e.g., `npm run build`) from an interactive agent session.
- Keep dependencies and lockfiles in sync. If you update deps, update the lockfile and restart relevant dev/test processes.
- Prefer small, focused commands for iterative work (run the specific tests you care about rather than the full suite when possible).
Expand All @@ -42,6 +46,7 @@ This file follows the AGENTS.md conventions (see https://agents.md/) and provide
---

## Tests & CI (repo conventions)

- **Follow Test-Driven Development (TDD): write tests before implementing features or bug fixes.** Add tests first and iterate until they pass; include the tests in the same PR as the implementation.
- **Achieve and maintain excellent test coverage.** Minimum thresholds: 100% lines, 95% statements/functions, 85% branches. Verify locally with `npm test -- --coverage` (or `npx jest --coverage`) and ensure CI coverage meets these requirements. PRs that reduce coverage below these thresholds will be rejected.
- **NEVER add coverage "ignore" comments (e.g., `/* istanbul ignore next */`) to artificially boost test coverage.** If code is truly difficult to test, adjust coverage thresholds or improve mocking strategies instead. Coverage ignore comments mask untested code and are not acceptable.
Expand All @@ -54,11 +59,13 @@ This file follows the AGENTS.md conventions (see https://agents.md/) and provide
### Testing GAS functions

Google Apps Script `.gs` files cannot be `require()`-d in Jest. To test GAS logic:

1. Extract the function to `src/index.js` and export it with `module.exports`.
2. Accept GAS services (`GmailApp`, `DocumentApp`, etc.) as parameters rather than accessing globals.
3. In the test file, create a wrapper function that injects `global.GmailApp`, `global.DocumentApp`, etc.

GAS `code.gs` files may optionally include a guard for Jest imports:

```js
if (typeof module !== 'undefined' && module.exports) {
module.exports = { ... };
Expand All @@ -68,9 +75,10 @@ if (typeof module !== 'undefined' && module.exports) {
---

## Code style & commits

- Follow repository style and lint rules.
- ALWAYS ensure `npm run lint` and `npm test` passes before committing.
- ALWAYS ensure `npm test -- --coverage` passes before committing.
- ALWAYS ensure `npm run lint` and `npm test` passes before committing.
- ALWAYS ensure `npm test -- --coverage` passes before committing.
- Keep commits small and include tests with behavior changes.
- Follow existing code style — no new linting or build tooling unless essential.

Expand All @@ -88,17 +96,20 @@ if (typeof module !== 'undefined' && module.exports) {
---

## Security & secrets

- Never commit secrets. Use GitHub Actions secrets or an external secret manager and document required secrets in `<agent-folder>/README.md`.
- Request maintainer review for agents requiring elevated permissions or access to sensitive data.

---

## How to use this file

- Agents will read the nearest AGENTS.md (this one is at the repo root).
- If a subproject needs different guidance, it may include its own `AGENTS.md` or a clear `README.md` explaining the differences.

---

## References

- AGENTS.md canonical guidance: https://agents.md/
- Example repository: https://github.com/agentsmd/agents.md/blob/main/AGENTS.md
2 changes: 1 addition & 1 deletion GEMINI.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
@./AGENTS.md
@./AGENTS.md
9 changes: 4 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,16 @@

![googleappscripts](header.jpg)


A collection of personal productivity scripts built on Google Apps Script. These tools are designed to automate repetitive tasks across Gmail, Google Drive, and Google Docs, helping you reclaim your time and keep your digital workspace organized.

Open sourced under the MIT License to help others build their own automation workflows.

## 📂 Script Catalog

| Script Name | Description | Documentation |
| :--- | :--- | :--- |
| Script Name | Description | Documentation |
| :--------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------ |
| **Gmail to Drive By Labels** | Automatically archives emails from specific Gmail labels into a Google Doc (text) and Google Drive Folder (attachments). Features robust text cleaning (removing quoted replies/legal footers) and smart content-based attachment de-duplication. | [View Readme](./src/gmail-to-drive-by-labels/README.md) |
| **Calendar to Sheets** | Syncs Google Calendar events into a Google Sheet, keeping rows up to date on changes and deletions. | [View Readme](./src/calendar-to-sheets/README.md) |
| **Calendar to Sheets** | Syncs Google Calendar events into a Google Sheet, keeping rows up to date on changes and deletions. | [View Readme](./src/calendar-to-sheets/README.md) |

## 🚀 Getting Started

Expand All @@ -37,4 +36,4 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file

---

*Note: These scripts are provided "as is". Always test on a small batch of data before running on important files.*
_Note: These scripts are provided "as is". Always test on a small batch of data before running on important files._
1 change: 1 addition & 0 deletions commitlint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = { extends: ['@commitlint/config-conventional'] }
64 changes: 64 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
const tseslint = require('@typescript-eslint/eslint-plugin')
const tsParser = require('@typescript-eslint/parser')
const prettierPlugin = require('eslint-plugin-prettier')
const prettierConfig = require('eslint-config-prettier')

/** @type {import('eslint').Linter.FlatConfig[]} */
module.exports = [
// TypeScript-ESLint flat/recommended base
...tseslint.configs['flat/recommended'],
{
files: ['**/*.{js,ts}'],
languageOptions: {
parser: tsParser,
ecmaVersion: 2022,
sourceType: 'commonjs',
globals: {
// Node.js globals
require: 'readonly',
module: 'readonly',
exports: 'readonly',
process: 'readonly',
__dirname: 'readonly',
__filename: 'readonly',
Buffer: 'readonly',
console: 'readonly',
// Jest globals
describe: 'readonly',
test: 'readonly',
expect: 'readonly',
beforeEach: 'readonly',
afterEach: 'readonly',
beforeAll: 'readonly',
afterAll: 'readonly',
jest: 'readonly',
// Google Apps Script globals
GmailApp: 'readonly',
DriveApp: 'readonly',
DocumentApp: 'readonly',
SpreadsheetApp: 'readonly',
CalendarApp: 'readonly',
Logger: 'readonly',
Session: 'readonly',
Utilities: 'readonly',
},
},
plugins: {
'@typescript-eslint': tseslint,
prettier: prettierPlugin,
},
rules: {
...tseslint.configs.recommended.rules,
...prettierConfig.rules,
'prettier/prettier': 'error',
'@typescript-eslint/no-require-imports': 'off',
'@typescript-eslint/no-unused-vars': [
'warn',
{ argsIgnorePattern: '^_' },
],
},
},
{
ignores: ['node_modules/', 'coverage/', '**/*.gs'],
},
]
25 changes: 19 additions & 6 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,32 @@
module.exports = {
preset: 'ts-jest/presets/js-with-ts',
testEnvironment: 'node',
setupFilesAfterEnv: ['<rootDir>/test-utils/setup.js'],
testMatch: ['**/tests/**/*.test.[jt]s?(x)'],
transform: {
'^.+\\.[jt]sx?$': [
'ts-jest',
{
tsconfig: {
allowJs: true,
strict: false,
},
diagnostics: false,
},
],
},
collectCoverageFrom: [
'src/**/*.{js,gs}',
'src/**/*.{js,ts,gs}',
'!**/node_modules/**',
'!**/coverage/**',
'!**/tests/**'
'!**/tests/**',
],
coverageThreshold: {
global: {
branches: 85,
functions: 95,
lines: 99,
statements: 95
}
}
};
statements: 95,
},
},
}
Loading
Loading