Adding production grade

This commit is contained in:
Moritz Graf 2026-04-25 13:43:01 +02:00
parent 37337ffb13
commit d7c89fda4e
5 changed files with 19 additions and 27 deletions

View File

@ -7,8 +7,8 @@ This directory contains Google Apps Script code configured as Infrastructure as
It automates the creation of email forwarding in Google Workspace by reading from a Google Sheet and dynamically creating/managing Workspace Groups. It automates the creation of email forwarding in Google Workspace by reading from a Google Sheet and dynamically creating/managing Workspace Groups.
## Tooling ## Tooling
- We use `@google/clasp` to manage the deployment of the `.ts` files to Google Apps Script. - We use `@google/clasp` to manage the deployment of the `.js` files to Google Apps Script. Note: do NOT write TypeScript as we bypass the local transpilation step.
- The entrypoint is `src/Code.ts`. - The entrypoint is `src/Code.js`.
- The manifest is `src/appsscript.json`. - The manifest is `src/appsscript.json`.
## Rules & Safeguards (CRITICAL) ## Rules & Safeguards (CRITICAL)

View File

@ -20,9 +20,9 @@ Before you can deploy, you must authenticate your local machine with your Google
## Step 2: Configuration ## Step 2: Configuration
You must configure the script to point to your specific Google Sheet. You must configure the script to point to your specific Google Sheet.
Open `src/Code.ts` and modify the `CONFIG` block at the top of the file: Open `src/Code.js` and modify the `CONFIG` block at the top of the file:
```typescript ```javascript
const CONFIG = { const CONFIG = {
// 1. The ID of the Google Sheet (found in the URL: https://docs.google.com/spreadsheets/d/<THIS_ID>/edit) // 1. The ID of the Google Sheet (found in the URL: https://docs.google.com/spreadsheets/d/<THIS_ID>/edit)
SPREADSHEET_ID: "YOUR_SHEET_ID_HERE", SPREADSHEET_ID: "YOUR_SHEET_ID_HERE",
@ -32,7 +32,7 @@ const CONFIG = {
// 3. The column numbers containing the data (1 = A, 2 = B, 3 = C, etc.) // 3. The column numbers containing the data (1 = A, 2 = B, 3 = C, etc.)
COL_SOURCE_ADDRESS: 2, COL_SOURCE_ADDRESS: 2,
COL_DESTINATION_ADDRESS: 3, COL_DESTINATION_ADDRESS: 6,
// 4. Your admin email for receiving reports // 4. Your admin email for receiving reports
ADMIN_EMAIL: "admin@haumdaucher.de", ADMIN_EMAIL: "admin@haumdaucher.de",
@ -75,4 +75,12 @@ The code is now in the cloud, but the background triggers need to be activated a
- Click "Allow" to grant access to the Admin Directory API, Gmail API, and Google Sheets. - Click "Allow" to grant access to the Admin Directory API, Gmail API, and Google Sheets.
5. Once authorized, the `setup` function will finish executing. It installs the background `onChange` trigger. 5. Once authorized, the `setup` function will finish executing. It installs the background `onChange` trigger.
## Step 5: Going Live
By default, the `CONFIG` block has `DRY_RUN: true`. This means the script will parse the sheet, output exactly what it *intends* to do to the logs, and email you a report, but it will **not** actually create, modify, or delete any Google Workspace Groups.
Once you have reviewed the dry-run execution logs and verified that the script correctly parses all rows and maps the email addresses as you expect:
1. Open `src/Code.js` and change `DRY_RUN: true` to `DRY_RUN: false`.
2. Run `clasp push` in your terminal to deploy the update.
3. Edit the spreadsheet one more time to trigger the live sync.
**You are done!** Whenever a new response is submitted to the configured Google Sheet via Forms, the script will automatically run in the background, reconcile the forwarding groups, and send you an email report. **You are done!** Whenever a new response is submitted to the configured Google Sheet via Forms, the script will automatically run in the background, reconcile the forwarding groups, and send you an email report.

View File

@ -7,9 +7,6 @@
"": { "": {
"name": "haumdaucher-mail-forwarding", "name": "haumdaucher-mail-forwarding",
"version": "1.0.0", "version": "1.0.0",
"dependencies": {
"typescript": "^6.0.3"
},
"devDependencies": { "devDependencies": {
"@google/clasp": "^2.4.2", "@google/clasp": "^2.4.2",
"@types/google-apps-script": "^1.0.83" "@types/google-apps-script": "^1.0.83"
@ -3343,19 +3340,6 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/typescript": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz",
"integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/undici-types": { "node_modules/undici-types": {
"version": "7.19.2", "version": "7.19.2",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz",

View File

@ -9,8 +9,5 @@
"devDependencies": { "devDependencies": {
"@google/clasp": "^2.4.2", "@google/clasp": "^2.4.2",
"@types/google-apps-script": "^1.0.83" "@types/google-apps-script": "^1.0.83"
},
"dependencies": {
"typescript": "^6.0.3"
} }
} }

View File

@ -20,6 +20,9 @@ const CONFIG = {
// The admin email that should receive the execution reports // The admin email that should receive the execution reports
ADMIN_EMAIL: "moritz@haumdaucher.de", ADMIN_EMAIL: "moritz@haumdaucher.de",
// The primary Workspace domain for validation and auto-appending
WORKSPACE_DOMAIN: "haumdaucher.de",
// Dry run mode. If true, the script will only log what it would do and send the email, // Dry run mode. If true, the script will only log what it would do and send the email,
// but will NOT actually create, update, or delete any groups in Workspace. // but will NOT actually create, update, or delete any groups in Workspace.
DRY_RUN: true, DRY_RUN: true,
@ -145,12 +148,12 @@ function readDesiredStateFromSheet() {
// 1. Auto-append domain to source if they just typed a name (e.g. "frederic") // 1. Auto-append domain to source if they just typed a name (e.g. "frederic")
if (source && !source.includes("@")) { if (source && !source.includes("@")) {
source += "@haumdaucher.de"; source += `@${CONFIG.WORKSPACE_DOMAIN}`;
} }
// 2. Validate source domain // 2. Validate source domain
if (source && !source.endsWith("@haumdaucher.de")) { if (source && !source.endsWith(`@${CONFIG.WORKSPACE_DOMAIN}`)) {
console.warn(`Row ${i + 1}: Skipped. Source address must belong to @haumdaucher.de domain. Found: '${source}'`); console.warn(`Row ${i + 1}: Skipped. Source address must belong to @${CONFIG.WORKSPACE_DOMAIN} domain. Found: '${source}'`);
continue; continue;
} }