feat(dev-experience): Automate npmrc configuration for dev vs prod

This PR introduces a new, automated workflow for developers to switch between the development (GitHub Packages) and production (npmjs.org) npm registries.

- A new script, , now automatically backs up and modifies the user's global  file.
-  has been updated with  and  scripts to run this new script.
-  has been updated to reflect this new, simplified, and automated process, removing all manual steps for the user.
This commit is contained in:
mkorwel
2025-10-21 17:51:41 -07:00
parent beed1515c2
commit 46b1c59a47
4 changed files with 371 additions and 1 deletions
+41
View File
@@ -279,6 +279,47 @@ npm run lint
- **Imports:** Pay special attention to import paths. The project uses ESLint to
enforce restrictions on relative imports between packages.
### Working with Pre-Release Versions (Development)
This project uses **GitHub Packages** to host development and pre-release
versions of the CLI. To install these versions, you must configure your
user-level `~/.npmrc` file to authenticate with the GitHub registry.
We have created a script to automate this process for you.
#### Setting Up Your Environment for Development (Recommended)
To automatically configure your environment to use the GitHub Packages registry,
run the following command:
```bash
npm run use-dev
```
This command will:
1. Check that you have the [GitHub CLI (`gh`)](https://cli.github.com/)
installed and that you are logged in.
2. **Back up** your existing `~/.npmrc` to `~/.npmrc.bak` if a backup doesn't
already exist.
3. **Automatically add** the necessary configuration to your `~/.npmrc` file.
This setup is required for installing pre-release packages with the
`@google-gemini` scope, both locally within a project and globally (e.g.,
`npm install -g @google-gemini/gemini-cli@<version>`).
#### Switching Back to the Production Registry
To automatically remove the development configuration and revert to the public
npm registry (`npmjs.org`), you can run:
```bash
npm run use-prod
```
This command will safely remove the lines added by the `use-dev` script from
your `~/.npmrc` file.
### Project Structure
- `packages/`: Contains the individual sub-packages of the project.
+179
View File
@@ -11,6 +11,7 @@
"packages/*"
],
"dependencies": {
"@google-gemini/gemini-cli": "^0.11.0-nightly.20251021.0e7b3951",
"@testing-library/dom": "^10.4.1",
"simple-git": "^3.28.0"
},
@@ -1457,6 +1458,159 @@
"uuid": "dist/bin/uuid"
}
},
"node_modules/@google-gemini/gemini-cli": {
"version": "0.11.0-nightly.20251021.0e7b3951",
"resolved": "https://npm.pkg.github.com/download/@google-gemini/gemini-cli/0.11.0-nightly.20251021.0e7b3951/4dd4a330cc3538ee3a6333f57c55a795634229fe",
"integrity": "sha512-qkQ7xfH7ih9kq17IV9lQcuvp6eJxiiBZUgpEm1I2fvRHjFRzzaszcEh6D8lVqoS10sGnuj2OnI4pBZW5a8UzEQ==",
"dependencies": {
"@google-gemini/gemini-cli-core": "0.11.0-nightly.20251021.0e7b3951"
},
"bin": {
"gemini": "bundle/gemini.js"
},
"engines": {
"node": ">=20"
}
},
"node_modules/@google-gemini/gemini-cli-core": {
"version": "0.11.0-nightly.20251021.0e7b3951",
"resolved": "https://npm.pkg.github.com/download/@google-gemini/gemini-cli-core/0.11.0-nightly.20251021.0e7b3951/612a101caade1e1df620ce84e3cf83fb66f46a12",
"integrity": "sha512-pu4KiOrjM5FVCH2LnqhirOWIHodg+giOl0nASROenzFaUD1G7/FYFT9T+OY8pmGtnrfI+dNrVolp3G5mI2Ij/w==",
"dependencies": {
"@google-cloud/logging": "^11.2.1",
"@google-cloud/opentelemetry-cloud-monitoring-exporter": "^0.21.0",
"@google-cloud/opentelemetry-cloud-trace-exporter": "^3.0.0",
"@google/genai": "1.16.0",
"@joshua.litt/get-ripgrep": "^0.0.2",
"@modelcontextprotocol/sdk": "^1.11.0",
"@opentelemetry/api": "^1.9.0",
"@opentelemetry/exporter-logs-otlp-grpc": "^0.203.0",
"@opentelemetry/exporter-logs-otlp-http": "^0.203.0",
"@opentelemetry/exporter-metrics-otlp-grpc": "^0.203.0",
"@opentelemetry/exporter-metrics-otlp-http": "^0.203.0",
"@opentelemetry/exporter-trace-otlp-grpc": "^0.203.0",
"@opentelemetry/exporter-trace-otlp-http": "^0.203.0",
"@opentelemetry/instrumentation-http": "^0.203.0",
"@opentelemetry/resource-detector-gcp": "^0.40.0",
"@opentelemetry/sdk-node": "^0.203.0",
"@types/glob": "^8.1.0",
"@types/html-to-text": "^9.0.4",
"@xterm/headless": "5.5.0",
"ajv": "^8.17.1",
"ajv-formats": "^3.0.0",
"chardet": "^2.1.0",
"diff": "^7.0.0",
"dotenv": "^17.1.0",
"fast-levenshtein": "^2.0.6",
"fast-uri": "^3.0.6",
"fdir": "^6.4.6",
"fzf": "^0.5.2",
"glob": "^10.4.5",
"google-auth-library": "^9.11.0",
"html-to-text": "^9.0.5",
"https-proxy-agent": "^7.0.6",
"ignore": "^7.0.0",
"marked": "^15.0.12",
"mime": "4.0.7",
"mnemonist": "^0.40.3",
"open": "^10.1.2",
"picomatch": "^4.0.1",
"shell-quote": "^1.8.3",
"simple-git": "^3.28.0",
"strip-ansi": "^7.1.0",
"tree-sitter-bash": "^0.25.0",
"undici": "^7.10.0",
"web-tree-sitter": "^0.25.10",
"ws": "^8.18.0"
},
"engines": {
"node": ">=20"
},
"optionalDependencies": {
"@lydell/node-pty": "1.1.0",
"@lydell/node-pty-darwin-arm64": "1.1.0",
"@lydell/node-pty-darwin-x64": "1.1.0",
"@lydell/node-pty-linux-x64": "1.1.0",
"@lydell/node-pty-win32-arm64": "1.1.0",
"@lydell/node-pty-win32-x64": "1.1.0",
"node-pty": "^1.0.0"
}
},
"node_modules/@google-gemini/gemini-cli-core/node_modules/ajv": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/@google-gemini/gemini-cli-core/node_modules/fdir": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
"license": "MIT",
"engines": {
"node": ">=12.0.0"
},
"peerDependencies": {
"picomatch": "^3 || ^4"
},
"peerDependenciesMeta": {
"picomatch": {
"optional": true
}
}
},
"node_modules/@google-gemini/gemini-cli-core/node_modules/ignore": {
"version": "7.0.5",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
"integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
"license": "MIT",
"engines": {
"node": ">= 4"
}
},
"node_modules/@google-gemini/gemini-cli-core/node_modules/json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"license": "MIT"
},
"node_modules/@google-gemini/gemini-cli-core/node_modules/mime": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/mime/-/mime-4.0.7.tgz",
"integrity": "sha512-2OfDPL+e03E0LrXaGYOtTFIYhiuzep94NSsuhrNULq+stylcJedcHdzHtz0atMUuGwJfFYs0YL5xeC/Ca2x0eQ==",
"funding": [
"https://github.com/sponsors/broofa"
],
"license": "MIT",
"bin": {
"mime": "bin/cli.js"
},
"engines": {
"node": ">=16"
}
},
"node_modules/@google-gemini/gemini-cli-core/node_modules/picomatch": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/@google/gemini-cli": {
"resolved": "packages/cli",
"link": true
@@ -5352,6 +5506,15 @@
"node": ">= 0.6"
}
},
"node_modules/accepts/node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/accepts/node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
@@ -8694,6 +8857,13 @@
"ms": "2.0.0"
}
},
"node_modules/express/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"license": "MIT",
"peer": true
},
"node_modules/express/node_modules/statuses": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
@@ -16323,6 +16493,15 @@
"node": ">= 0.6"
}
},
"node_modules/type-is/node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/type-is/node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+4 -1
View File
@@ -55,7 +55,9 @@
"telemetry": "node scripts/telemetry.js",
"check:lockfile": "node scripts/check-lockfile.js",
"clean": "node scripts/clean.js",
"pre-commit": "node scripts/pre-commit.js"
"pre-commit": "node scripts/pre-commit.js",
"use-dev": "node scripts/configure-registry.js dev",
"use-prod": "node scripts/configure-registry.js prod"
},
"overrides": {
"wrap-ansi": "9.0.2",
@@ -111,6 +113,7 @@
"yargs": "^17.7.2"
},
"dependencies": {
"@google-gemini/gemini-cli": "^0.11.0-nightly.20251021.0e7b3951",
"@testing-library/dom": "^10.4.1",
"simple-git": "^3.28.0"
},
+147
View File
@@ -0,0 +1,147 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import fs from 'node:fs';
import path from 'node:path';
import os from 'node:os';
import { execSync } from 'node:child_process';
const arg = process.argv[2];
const homedir = os.homedir();
const npmrcPath = path.join(homedir, '.npmrc');
const backupPath = path.join(homedir, '.npmrc.bak');
const GITHUB_REGISTRY_URL = 'https://npm.pkg.github.com/';
const GITHUB_SCOPE = '@google-gemini';
const REGISTRY_LINE = `${GITHUB_SCOPE}:registry=${GITHUB_REGISTRY_URL}`;
function checkGhCli() {
try {
execSync('gh --version', { stdio: 'ignore' });
return true;
} catch (_e) {
console.error(
'Error: The GitHub CLI (`gh`) is not installed or not in your PATH.',
);
console.error('Please install it to continue: https://cli.github.com/');
return false;
}
}
function getGhAuthStatus() {
try {
execSync('gh auth status', { stdio: 'ignore' });
return true;
} catch (_e) {
console.error('Error: You are not logged in to the GitHub CLI (`gh`).');
console.error(
"Please run `gh auth login` and ensure you grant the 'read:packages' and 'write:packages' scopes.",
);
return false;
}
}
function getGhAuthToken() {
try {
const token = execSync('gh auth token', {
encoding: 'utf8',
}).trim();
if (!token) {
throw new Error('No token returned from `gh auth token`');
}
return token;
} catch (_e) {
console.error('Error: Failed to retrieve GitHub auth token.');
console.error(
'Please ensure you are logged in (`gh auth status`) and have the correct permissions.',
);
return null;
}
}
function setupDev() {
console.log(
'Configuring your global ~/.npmrc for `dev` (GitHub Packages)...',
);
if (!checkGhCli() || !getGhAuthStatus()) {
process.exit(1);
}
const token = getGhAuthToken();
if (!token) {
process.exit(1);
}
const AUTH_LINE = `//npm.pkg.github.com/:_authToken=${token}`;
let npmrcContent = '';
if (fs.existsSync(npmrcPath)) {
npmrcContent = fs.readFileSync(npmrcPath, 'utf-8');
if (npmrcContent.includes(REGISTRY_LINE)) {
console.log('✅ Your ~/.npmrc file is already configured for dev.');
return;
}
// Create a backup if one doesn't already exist
if (!fs.existsSync(backupPath)) {
fs.copyFileSync(npmrcPath, backupPath);
console.log(`Backed up your existing configuration to ${backupPath}`);
}
}
const newContent = [
npmrcContent,
`\n# Added by gemini-cli for dev environment`,
REGISTRY_LINE,
AUTH_LINE,
]
.join('\n')
.trim();
fs.writeFileSync(npmrcPath, newContent + '\n');
console.log(`✅ Successfully updated your ~/.npmrc file.`);
console.log(
'You can now install pre-release packages, e.g., `npm install -g @google-gemini/gemini-cli@<version>`',
);
}
function setupProd() {
console.log('Configuring your global ~/.npmrc for `prod` (npmjs.org)...');
if (!fs.existsSync(npmrcPath)) {
console.log('No ~/.npmrc file found. Already configured for prod.');
return;
}
let npmrcContent = fs.readFileSync(npmrcPath, 'utf-8');
const lines = npmrcContent.split('\n');
const filteredLines = lines.filter(
(line) =>
!line.includes('npm.pkg.github.com') &&
!line.includes('@google-gemini:registry') &&
!line.includes('# Added by gemini-cli'),
);
if (lines.length === filteredLines.length) {
console.log('✅ Your ~/.npmrc file is already configured for prod.');
return;
}
fs.writeFileSync(npmrcPath, filteredLines.join('\n').trim() + '\n');
console.log(`✅ Successfully cleaned dev configuration from ~/.npmrc.`);
if (fs.existsSync(backupPath)) {
console.log(`Your original configuration was saved to ${backupPath}`);
}
}
if (arg === 'dev') {
setupDev();
} else if (arg === 'prod') {
setupProd();
} else {
console.error('Invalid argument. Please use `dev` or `prod`.');
console.error('Usage: node scripts/configure-registry.js <dev|prod>');
process.exit(1);
}