-
Notifications
You must be signed in to change notification settings - Fork 81
Plugin testing #152
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
christhompsongoogle
wants to merge
4
commits into
main
Choose a base branch
from
agplugin
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Plugin testing #152
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| The plugin is just dropped in a folder - so the easiest way is to symlink the working source from your app folder like so: | ||
|
|
||
| `ln -s ~/firebase/agent-skills/.antigravity-plugin ~/myappdirectory/.agents/plugins/firebase-antigravity-plugin` | ||
|
|
||
| Pre-flight checks: | ||
| - ensure you're not using corp - this prevents new project creation (you'll get a 401) | ||
| - `gcloud config set account christhompsonfirebase@gmail.com` | ||
| - Login for ADC (if you continue to get 401 ensure you check the boxes on login) | ||
| - `gcloud auth application-default login` | ||
|
|
||
| You'll need to accept Firebase TOS for the user you're using in gcloud. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| - Update new Rules logic once chliang@ is done updating the firebase skill. | ||
| - Add a gcloud rules deploy (or agent tool for making the API call) | ||
| - Add a reference to scoring rules | ||
| - Write the gcloud deploy logic | ||
| - Figure out how to test this thing | ||
| - Write system instructions | ||
| - MCP server support | ||
| - TODO: Add MCP for deployment (currently using gcloud CLI for v0) | ||
| - Sidecar ideas needed https://antigravity.google/docs/sidecars | ||
| - How about a rules analyzer that runs antagonistically to the main model | ||
| - wait for cloud run deployment and reports success or failure to the main model | ||
| - "Sidecars can use the agentapi CLI to programmatically interact with Antigravity. The executable is automatically added to the sidecar’s path and available as agentapi." | ||
| - Allegedly there's a nodejs environment shipped with AG via electron | ||
| - Emulators? Interesting idea | ||
| - Hooks | ||
| - Check rules score before deploying rules? | ||
| - Does an MCP tool command work? The docs seem to suggest that only the AGY tools are matchable. Gemini says MCP tools work | ||
| - Validate hooking on MCP or just gcloud commands | ||
| - Metrics tracking - lots of opportunities here | ||
| - Subagents ? can we have a rules subagent? | ||
| - Alternative to side-car is the polling subagent that checks when (for example) cloud run deploy completes | ||
| - Model assumes node for npx create-vite - maybe we can't get away from Node for web apps. | ||
| - Curl requests are scary looking for the user to approve, need to wrap in gocloud | ||
| - initializing firebase - need a firebase init in the plugin or firebase.json template inside gocloud | ||
| - npx firebase apps:sdkconfig WEB --project com-example-noteapp-f9b56 | ||
| Deployment environments: | ||
| - Plugin works in CLI + AG2.0 + AGYIDE | ||
| - Antigravity (Google’s AI coding tool powered by Gemini) as an assistant directly inside Claude Code | ||
|
|
||
| Gcloud commands to add: | ||
| - npx firebase apps:sdkconfig WEB --project com-example-noteapp-f9b56 | ||
| - This is in response to printing the configData base64 object in the provisioning response. | ||
| - Create app provisioning API | ||
| - Encapsulates the cURL command, can poll for operation completion, return firebaseconfig object | ||
| - Create database after the fact is necessary? Can provisioning API do it? likely | ||
| - 1:1 DB model? in which case no need provisioning API | ||
| - `gcloud firestore databases create --project=com-example-noteapp-f9b56 --location=us-central1 --type=firestore-native --edition=enterprise --enable-firestore-data-access --enable-realtime-updates` | ||
| - Upgrade to blaze using billing ID | ||
| Omit docker: | ||
| """ | ||
| Yes, you absolutely can omit the Dockerfile! | ||
|
|
||
| When you run gcloud run deploy --source . without a Dockerfile present, Cloud Run uses Google Cloud Buildpacks to automatically inspect your code, detect that it's a Node.js project, and build a container for you behind the scenes. | ||
|
|
||
| However, since this is a Vite-built React single-page application (SPA), omitting the Dockerfile requires a small tradeoff: | ||
|
|
||
| By default, the Node.js Buildpack expects a traditional web server and looks for an npm start script. To make it work without a Dockerfile, we would need to: | ||
|
|
||
| Install a static file server (like npm install serve) | ||
| Update the package.json to have a start script: "start": "serve -s dist" | ||
| The reason I explicitly generated the Dockerfile is because it allows us to use an ultra-lightweight NGINX image. NGINX is heavily optimized for serving static files and makes it trivial to add the try_files $uri $uri/ /index.html; configuration, which is required so that client-side routing (like React Router) doesn't return 404 Not Found errors when a user refreshes the page. | ||
|
|
||
| If you prefer to keep the repository clean of Docker-related files, we can easily delete the Dockerfile, install serve, and rely on Buildpacks instead! Let me know if you'd like me to make that switch | ||
| """ | ||
|
|
||
| Billing | ||
| - Probably don't want to block on billing, which is necessary for cloud run, but not necessary to run local | ||
| - Does the provisioning API let me attach a billing instrument after project creation? need to test | ||
|
|
||
|
|
||
| Test/Questions: | ||
| - provisioning API attach billing | ||
| - provisioning API create DB after project provisioning initially | ||
| - Does the AIS cloud run deploy use docker? | ||
|
|
||
| TODO rn: | ||
| Enable google auth provider. | ||
| Ensure the project in gcloud matches the one in deployment | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| { | ||
| "firebase-rules-guard": { | ||
| "PreToolUse": [ | ||
| { | ||
| "matcher": "run_command|run_shell_command", | ||
| "hooks": [ | ||
| { | ||
| "command": "node ./node_hooks/firebase_rules_deploy_guard.js", | ||
| "timeout": 30 | ||
| } | ||
| ] | ||
| } | ||
| ] | ||
| } | ||
| } |
65 changes: 65 additions & 0 deletions
65
.antigravity-plugin/node_hooks/firebase_rules_deploy_guard.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| #!/usr/bin/env node | ||
| const fs = require('fs'); | ||
| const path = require('path'); | ||
|
|
||
| function evaluateRules(rulesContent) { | ||
| let score = 5; | ||
|
|
||
| // Check for wildcards allow-alls (e.g., allow read, write: if true;) | ||
| if (/allow\s+[^:]+:\s*if\s+true\s*;/.test(rulesContent)) { | ||
| score -= 3; | ||
| } | ||
|
|
||
| // Check for unauthenticated write permissions | ||
| if (/allow\s+write:\s*if\s+request\.auth\s*==\s*null/.test(rulesContent)) { | ||
| score -= 2; | ||
| } | ||
|
|
||
| return score; | ||
| } | ||
|
|
||
| function main() { | ||
| let inputBuffer = ''; | ||
|
|
||
| // Read the JSON payload from the AG harness via stdin | ||
| process.stdin.on('data', chunk => { inputBuffer += chunk; }); | ||
|
|
||
| process.stdin.on('end', () => { | ||
| let payload; | ||
| try { | ||
| payload = JSON.parse(inputBuffer); | ||
| } catch (e) { | ||
| // Safe fallback: Allow execution if stdin can't be parsed | ||
| console.log(JSON.stringify({ decision: 'allow' })); | ||
| process.exit(0); | ||
| } | ||
|
|
||
| const toolCall = payload.toolCall || {}; | ||
| const cmdLine = (toolCall.args || {}).CommandLine || ''; | ||
| const cwd = (toolCall.args || {}).Cwd || process.cwd(); | ||
|
|
||
| // Intercept Firestore deployments | ||
| if (cmdLine.includes('firebase deploy') && cmdLine.includes('firestore')) { | ||
| const rulesPath = path.join(cwd, 'firestore.rules'); | ||
|
|
||
| if (fs.existsSync(rulesPath)) { | ||
| const rulesContent = fs.readFileSync(rulesPath, 'utf8'); | ||
| const score = evaluateRules(rulesContent); | ||
| const threshold = 4; | ||
|
|
||
| if (score < threshold) { | ||
| const response = { | ||
| decision: 'deny', | ||
| reason: `🚨 HOOK EXECUTED: Deployment blocked due to insecure rules! Score: ${score}/5 (Required: ${threshold}+) 🚨` | ||
| }; | ||
| console.log(JSON.stringify(response)); | ||
| process.exit(0); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| console.log(JSON.stringify({ decision: 'allow' })); | ||
| }); | ||
| } | ||
|
|
||
| main(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| { | ||
| "name": "firebase-antigravity-plugin", | ||
| "version": "0.1.0", | ||
| "description": "Builders Firebase plugin for Antigravity.", | ||
| "skills": "./skills", | ||
| "rules": "./rules", | ||
| "hooks": "hooks.json" | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Whenever you update an application that uses firestore is modified, be sure to update your rules following the firebase-write-rules skill. After that you can deploy using the firebase-rules-deploy skill. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| # Firebase Plugin System Instructions | ||
|
|
||
| When working within this project, you are utilizing the Antigravity Firebase plugin. Follow these constraints: | ||
|
|
||
| 1. **Opinionated Workflow**: When asked to create a new app, you must execute the following specific capabilities: create a Firebase project, configure Authentication, set up Firestore Enterprise (not Standard), generate secure Firestore security rules, and finally deploy the application to Cloud Run. | ||
| 2. **Project Creation** Can be done via the `create-project` skill. | ||
| 3. **Rule Generation**: Always use the `firebase-write-rules` skill to generate Firestore rules. | ||
| 4. **Rule Deployment**: Use the `firebase-rules-deploy` skill. | ||
| 5. **Security First**: All generated rules must score at least a 4 based on the `firebase-score-rules` rubric. Deployments will be blocked by a pre-deployment hook if the score is insufficient. | ||
| 6. **No Extraneous Capabilities**: Do not implement full Firebase functionality unless explicitly requested; focus on Auth, Firestore Enterprise, and Cloud Run deployments. | ||
| 7. **Cloud Run is the ideal deployment surface.** Don't ask the user where to deploy the app. If not specified by the user, use Cloud Run. Instructions can be found in the `deploy-cloud-run` skill. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| --- | ||
| name: add-billing | ||
| description: A skill to add billing to a Firebase project. Use this when you need to add billing to a Firebase project (e.g. before deploying a Cloud Run app). | ||
| --- | ||
|
|
||
| `gcloud billing accounts list` | ||
|
|
||
| If no results, you'll need to create a billing account. Forward them to the console to create a billing account: | ||
| https://console.firebase.google.com/u/3/project/<projectId> | ||
|
|
||
| `gcloud billing accounts list` may take 10 seconds to update with the billing account. | ||
|
|
||
| Then, | ||
|
|
||
| `gcloud beta billing projects link <projectId> --billing-account=<accountId>` | ||
|
|
||
| gcloud beta billing projects link com-example-noteapp-f9b56 --billing-account=013F6F-47D260-E203AC |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| --- | ||
| name: create-project | ||
| description: A skill to create a Firebase project. Use this when you need to create a new Firebase project. | ||
| --- | ||
|
|
||
| Use the following curl command to create a new project: | ||
|
|
||
| curl -X POST https://firebase.googleapis.com/v1alpha/firebase:provisionFirebaseApp \ | ||
| -H "Authorization: Bearer $(gcloud auth print-access-token)" \ | ||
| -H "X-Goog-User-Project: isdhaksdflashgfal" \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{ | ||
| "app_namespace": "com.example.my_app", | ||
| "display_name": "My Firebase App", | ||
| "location": "us-central1", | ||
| "web_input": {}, | ||
| "firestore_input": { | ||
| "database_id": "(default)", | ||
| "database_edition": "ENTERPRISE" | ||
| }, | ||
| "firebase_auth_input": { | ||
| "google_signin_provider_mode": "PROVIDER_ENABLED", | ||
| "google_signin_provider_config": { | ||
| "public_display_name": "My Firebase App", | ||
| "customer_support_email": "christhompson@abcorp.com", | ||
| "oauth_brand_mode": "TEST" | ||
| } | ||
| } | ||
| }' | ||
|
|
||
|
|
||
| Create a subagent to poll this endpoint for the operation result and return the project name and the project number: | ||
| ``` | ||
| curl -X GET "https://firebase.googleapis.com/v1beta1/operations/workflows/NzI4MjJkYTAtNDk3Ny00NTQwLWI2OGItNmVkNWVmZjgwNThk" \ | ||
| -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" | ||
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| --- | ||
| name: deploy-cloud-run | ||
| description: A skill to deploy a web application to Cloud Run. Use this when you need to deploy a web application to Cloud Run. | ||
| --- | ||
|
|
||
| # Deploy Application to Cloud Run | ||
|
|
||
| When asked to deploy the application to Cloud Run, follow these instructions: | ||
|
|
||
| 1. **Prerequisites**: Ensure you are in the root directory of the web application. | ||
| - You'll need to ensure that billing is enabled. To check if billing is enabled and attach a billing account (if you have one) use the "add-billing" skill. | ||
| 2. **Prepare Deployment Assets**: Before running the deployment command, copy the `Dockerfile` provided in this skill's `resources/` directory into the root of the web application. | ||
| 3. **Deployment Command**: Use the `gcloud run deploy` command to deploy directly from source. | ||
| 4. **Arguments**: | ||
| - `--source .`: Use the current directory as the source for the build. | ||
| - `--allow-unauthenticated`: Ensure the deployed app is publicly viewable by third parties. | ||
| - Supply a `SERVICE_NAME` and `REGION` (e.g., `us-central1`), picking sensible defaults if none are provided. | ||
| 5. **Cleanup**: After the deployment completes (successfully or not), **delete** the `Dockerfile` from the web application's root directory to keep the source tree clean. | ||
|
|
||
| **Example Command Sequence**: | ||
| ```bash | ||
| cp .agents/plugins/firebase-antigravity-plugin/skills/deploy-cloud-run/resources/Dockerfile ./Dockerfile | ||
| gcloud run deploy my-web-app --source . --region us-central1 --allow-unauthenticated | ||
| rm ./Dockerfile | ||
| ``` |
23 changes: 23 additions & 0 deletions
23
.antigravity-plugin/skills/deploy-cloud-run/resources/Dockerfile
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| # Build environment | ||
| FROM node:20-alpine as build | ||
| WORKDIR /app | ||
| COPY package*.json ./ | ||
| RUN npm install | ||
| COPY . . | ||
| RUN npm run build | ||
|
|
||
| # Production environment | ||
| FROM nginx:alpine | ||
| COPY --from=build /app/dist /usr/share/nginx/html | ||
| # Custom nginx config for single page apps | ||
| RUN echo 'server { \ | ||
| listen 8080; \ | ||
| location / { \ | ||
| root /usr/share/nginx/html; \ | ||
| index index.html index.htm; \ | ||
| try_files $uri $uri/ /index.html; \ | ||
| } \ | ||
| }' > /etc/nginx/conf.d/default.conf | ||
|
|
||
| EXPOSE 8080 | ||
| CMD ["nginx", "-g", "daemon off;"] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| --- | ||
| name: firebase-score-rules | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| description: A skill to evaluate how secure Firestore security rules are. Use this when Firestore security rules are updated to ensure that the generated rules are extremely secure and robust. | ||
| --- | ||
|
|
||
| # Overview | ||
|
|
||
| This skill acts as an auditor for Firebase Security Rules, evaluating them | ||
| against a rigorous set of criteria to ensure they are secure, robust, and | ||
| correctly implemented. | ||
|
|
||
| # Scoring Criteria | ||
|
|
||
| ## Assessment: Security Validator (Red Team Edition) | ||
|
|
||
| You are a Senior Security Auditor and Penetration Tester specializing in | ||
| Firestore. Your goal is to find "the hole in the wall." Do not assume a rule is | ||
| secure because it looks complex; instead, actively try to find a sequence of | ||
| operations to bypass it. | ||
|
|
||
| ### Mandatory Audit Checklist: | ||
|
|
||
| 1. **The Update Bypass:** Compare 'create' and 'update' rules. Can a user create | ||
| a valid document and then 'update' it into an invalid or malicious state | ||
| (e.g., changing their role, bypassing size limits, or corrupting data types)? | ||
| 1. **Authority Source:** Does the security rely on user-provided data | ||
| (request.resource.data) for sensitive fields like 'role', 'isAdmin', or | ||
| 'ownerId'? Carefully consider the source for that authority. | ||
| 1. **Business Logic vs. Rules:** Does the rule set actually support the app's | ||
| purpose? (e.g., In a collaboration app, can collaborators actually read the | ||
| data? If not, the rules are "broken" or will force insecure workarounds). | ||
| 1. **Storage Abuse:** Are there string length or array size limits? If not, | ||
| label it as a "Resource Exhaustion/DoS" risk. | ||
| 1. **Type Safety:** Are fields checked with 'is string', 'is int', or 'is | ||
| timestamp'? | ||
| 1. **Field-Level vs. Identity-Level Security:** Be careful with rules that use | ||
| \`hasOnly()\` or \`diff()\`. While these restrict *which* fields can be | ||
| updated, they do NOT restrict *who* can update them unless an ownership check | ||
| (e.g., \`resource.data.uid == request.auth.uid\`) is also present. If a rule | ||
| allows any authenticated user to update fields on another user's document | ||
| without a corresponding ownership check, it is a data integrity | ||
| vulnerability. | ||
|
|
||
| ### Admin Bootstrapping & Privileges: | ||
|
|
||
| The admin bootstrapping process is limited in this app. If the rules use a | ||
| single hardcoded admin email (e.g., checking request.auth.token.email == | ||
| 'admin@example.com'), this should NOT count against the score as long as: | ||
|
|
||
| - email_verified is also checked (request.auth.token.email_verified == true). | ||
| - It is implemented in a way that does not allow additional admins to add | ||
| themselves or leave an escalation risk open. | ||
|
|
||
| ### Scoring Criteria (1-5): | ||
|
|
||
| - **1 (Critical):** Unauthorized data access (leaks), privilege escalation, or | ||
| total validation bypass. | ||
| - **2 (Major):** Broken business logic, self-assigned roles, bypass of controls. | ||
| - **3 (Moderate):** PII exposure (e.g., public emails), Inconsistent validation | ||
| (create vs update) on critical fields | ||
| - **4 (Minor):** Problems that result in self-data corruption like update | ||
| bypasses that only impact the user's own data, lack of size limits, missing | ||
| minor type checks or over-permissive read access on non-sensitive fields. | ||
| - **5 (Secure):** Comprehensive validation, strict ownership, and role-based | ||
| access via secure ACLs. | ||
|
|
||
| Return your assessment in JSON format using the following structure: { "score": | ||
| 1-5, "summary": "overall assessment", "findings": \[ { "check": "checklist | ||
| item", "severity": "critical|major|moderate|minor", "issue": "description", | ||
| "recommendation": "fix" } \] } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| --- | ||
| name: firebase-rules-deploy | ||
| description: A skill to deploy Firestore rules. Use this when you need to deploy Firestore rules. | ||
| --- | ||
|
|
||
| # Deploy Firestore Rules | ||
|
|
||
| Prior to deploying rules we need to make sure our gcloud email and firebase email match. If not use the following: | ||
|
|
||
| `firebase login` will print the currently logged in and selected account. | ||
| `gcloud config get-value account` similarly will print the currently logged in account. | ||
|
|
||
| Use `firebase deploy --only firestore:rules --project=<project_id>`. All other deployments should use the `gcloud` CLI. |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid hardcoding personal email addresses in the testing instructions. Use a generic placeholder instead.