mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-19 09:33:53 +00:00
Release and Packaging: Clean up (#3489)
This commit is contained in:
@@ -1,65 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { readFileSync } from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
function readPackageJson(dir) {
|
||||
const p = path.join(dir, 'package.json');
|
||||
return JSON.parse(readFileSync(p, 'utf-8'));
|
||||
}
|
||||
|
||||
const root = readPackageJson('.');
|
||||
const cli = readPackageJson('packages/cli');
|
||||
const core = readPackageJson('packages/core');
|
||||
|
||||
const errors = [];
|
||||
|
||||
console.log('Checking version consistency...');
|
||||
|
||||
// 1. Check that all package versions are the same.
|
||||
if (root.version !== cli.version || root.version !== core.version) {
|
||||
errors.push(
|
||||
`Version mismatch: root (${root.version}), cli (${cli.version}), core (${core.version})`,
|
||||
);
|
||||
} else {
|
||||
console.log(`- All packages are at version ${root.version}.`);
|
||||
}
|
||||
|
||||
// 2. Check that the cli's dependency on core matches the core version.
|
||||
const coreDepVersion = cli.dependencies['@google/gemini-cli-core'];
|
||||
const expectedCoreVersion = `^${core.version}`;
|
||||
if (
|
||||
coreDepVersion !== expectedCoreVersion &&
|
||||
coreDepVersion !== 'file:../core'
|
||||
) {
|
||||
errors.push(
|
||||
`CLI dependency on core is wrong: expected ${expectedCoreVersion} or "file:../core", got ${coreDepVersion}`,
|
||||
);
|
||||
} else {
|
||||
console.log(`- CLI dependency on core (${coreDepVersion}) is correct.`);
|
||||
}
|
||||
|
||||
// 3. Check that the sandbox image tag matches the root version.
|
||||
const imageUri = root.config.sandboxImageUri;
|
||||
const imageTag = imageUri.split(':').pop();
|
||||
if (imageTag !== root.version) {
|
||||
errors.push(
|
||||
`Sandbox image tag mismatch: expected ${root.version}, got ${imageTag}`,
|
||||
);
|
||||
} else {
|
||||
console.log(`- Sandbox image tag (${imageTag}) is correct.`);
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
console.error('\nVersion consistency checks failed:');
|
||||
for (const error of errors) {
|
||||
console.error(`- ${error}`);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log('\nAll version checks passed!');
|
||||
@@ -1,11 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
// esbuild-banner.js
|
||||
import { createRequire } from 'module';
|
||||
const require = createRequire(import.meta.url);
|
||||
globalThis.__filename = require('url').fileURLToPath(import.meta.url);
|
||||
globalThis.__dirname = require('path').dirname(globalThis.__filename);
|
||||
@@ -1,75 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
// Example proxy server that listens on :::8877 and only allows HTTPS connections to example.com.
|
||||
// Set `GEMINI_SANDBOX_PROXY_COMMAND=scripts/example-proxy.js` to run proxy alongside sandbox
|
||||
// Test via `curl https://example.com` inside sandbox (in shell mode or via shell tool)
|
||||
|
||||
import http from 'http';
|
||||
import net from 'net';
|
||||
import { URL } from 'url';
|
||||
import console from 'console';
|
||||
|
||||
const PROXY_PORT = 8877;
|
||||
const ALLOWED_DOMAINS = ['example.com', 'googleapis.com'];
|
||||
const ALLOWED_PORT = '443';
|
||||
|
||||
const server = http.createServer((req, res) => {
|
||||
// Deny all requests other than CONNECT for HTTPS
|
||||
console.log(
|
||||
`[PROXY] Denying non-CONNECT request for: ${req.method} ${req.url}`,
|
||||
);
|
||||
res.writeHead(405, { 'Content-Type': 'text/plain' });
|
||||
res.end('Method Not Allowed');
|
||||
});
|
||||
|
||||
server.on('connect', (req, clientSocket, head) => {
|
||||
// req.url will be in the format "hostname:port" for a CONNECT request.
|
||||
const { port, hostname } = new URL(`http://${req.url}`);
|
||||
|
||||
console.log(`[PROXY] Intercepted CONNECT request for: ${hostname}:${port}`);
|
||||
|
||||
if (
|
||||
ALLOWED_DOMAINS.some(
|
||||
(domain) => hostname == domain || hostname.endsWith(`.${domain}`),
|
||||
) &&
|
||||
port === ALLOWED_PORT
|
||||
) {
|
||||
console.log(`[PROXY] Allowing connection to ${hostname}:${port}`);
|
||||
|
||||
// Establish a TCP connection to the original destination.
|
||||
const serverSocket = net.connect(port, hostname, () => {
|
||||
clientSocket.write('HTTP/1.1 200 Connection Established\r\n\r\n');
|
||||
// Create a tunnel by piping data between the client and the destination server.
|
||||
serverSocket.write(head);
|
||||
serverSocket.pipe(clientSocket);
|
||||
clientSocket.pipe(serverSocket);
|
||||
});
|
||||
|
||||
serverSocket.on('error', (err) => {
|
||||
console.error(`[PROXY] Error connecting to destination: ${err.message}`);
|
||||
clientSocket.end(`HTTP/1.1 502 Bad Gateway\r\n\r\n`);
|
||||
});
|
||||
} else {
|
||||
console.log(`[PROXY] Denying connection to ${hostname}:${port}`);
|
||||
clientSocket.end('HTTP/1.1 403 Forbidden\r\n\r\n');
|
||||
}
|
||||
|
||||
clientSocket.on('error', (err) => {
|
||||
// This can happen if the client hangs up.
|
||||
console.error(`[PROXY] Client socket error: ${err.message}`);
|
||||
});
|
||||
});
|
||||
|
||||
server.listen(PROXY_PORT, () => {
|
||||
const address = server.address();
|
||||
console.log(`[PROXY] Proxy listening on ${address.address}:${address.port}`);
|
||||
console.log(
|
||||
`[PROXY] Allowing HTTPS connections to domains: ${ALLOWED_DOMAINS.join(', ')}`,
|
||||
);
|
||||
});
|
||||
@@ -1,82 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
// ES module equivalent of __dirname
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
const cliPackageJsonPath = path.resolve(
|
||||
__dirname,
|
||||
'../packages/cli/package.json',
|
||||
);
|
||||
const cliPackageJson = JSON.parse(fs.readFileSync(cliPackageJsonPath, 'utf8'));
|
||||
|
||||
// Get version from root package.json (accessible via env var in npm scripts)
|
||||
const version = process.env.npm_package_version;
|
||||
|
||||
// Get Docker registry and image name directly from PUBLISH_ environment variables.
|
||||
// These are expected to be set by the CI/build environment.
|
||||
const containerImageRegistry = process.env.SANDBOX_IMAGE_REGISTRY;
|
||||
const containerImageName = process.env.SANDBOX_IMAGE_NAME;
|
||||
|
||||
if (!version || !containerImageRegistry || !containerImageName) {
|
||||
console.error(
|
||||
'Error: Missing required environment variables. Need: ' +
|
||||
'npm_package_version, SANDBOX_IMAGE_REGISTRY, and SANDBOX_IMAGE_NAME.',
|
||||
);
|
||||
console.error(
|
||||
'These should be passed from the CI environment (e.g., Cloud Build substitutions) ' +
|
||||
'to the npm publish:release script.',
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const containerImageUri = `${containerImageRegistry}/${containerImageName}:${version}`;
|
||||
|
||||
// Add or update fields in cliPackageJson.config to store this information
|
||||
if (!cliPackageJson.config) {
|
||||
cliPackageJson.config = {};
|
||||
}
|
||||
cliPackageJson.config.sandboxImageUri = containerImageUri;
|
||||
|
||||
fs.writeFileSync(
|
||||
cliPackageJsonPath,
|
||||
JSON.stringify(cliPackageJson, null, 2) + '\n',
|
||||
);
|
||||
console.log(
|
||||
`Updated ${path.relative(process.cwd(), cliPackageJsonPath)} with Docker image details:`,
|
||||
);
|
||||
console.log(` URI: ${containerImageUri}`);
|
||||
console.log(` Registry: ${containerImageRegistry}`);
|
||||
console.log(` Image Name: ${containerImageName}`);
|
||||
|
||||
// Copy README.md to packages/cli
|
||||
const rootReadmePath = path.resolve(__dirname, '../README.md');
|
||||
const cliReadmePath = path.resolve(__dirname, '../packages/cli/README.md');
|
||||
|
||||
try {
|
||||
fs.copyFileSync(rootReadmePath, cliReadmePath);
|
||||
console.log('Copied root README.md to packages/cli/');
|
||||
} catch (err) {
|
||||
console.error('Error copying README.md:', err);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Copy README.md to packages/cli
|
||||
const rootLicensePath = path.resolve(__dirname, '../LICENSE');
|
||||
const cliLicensePath = path.resolve(__dirname, '../packages/cli/LICENSE');
|
||||
|
||||
try {
|
||||
fs.copyFileSync(rootLicensePath, cliLicensePath);
|
||||
console.log('Copied root LICENSE to packages/cli/');
|
||||
} catch (err) {
|
||||
console.error('Error copying LICENSE:', err);
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
const packageJsonPath = path.resolve(process.cwd(), 'package.json');
|
||||
const readmePath = path.resolve(process.cwd(), 'README.md');
|
||||
const licensePath = path.resolve(process.cwd(), 'LICENSE');
|
||||
|
||||
const errors = [];
|
||||
|
||||
// 1. Check for package.json and the 'repository' field
|
||||
// Required for publishing through wombat-dressing-room
|
||||
if (!fs.existsSync(packageJsonPath)) {
|
||||
errors.push(`Error: package.json not found in ${process.cwd()}`);
|
||||
} else {
|
||||
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
||||
if (
|
||||
!packageJson.repository ||
|
||||
typeof packageJson.repository !== 'object' ||
|
||||
packageJson.repository.type !== 'git' ||
|
||||
!packageJson.repository.url.includes('google-gemini/gemini-cli')
|
||||
) {
|
||||
errors.push(
|
||||
`Error: The "repository" field in ${packageJsonPath} must be an object pointing to the "google-gemini/gemini-cli" git repository.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Check for README.md
|
||||
if (!fs.existsSync(readmePath)) {
|
||||
errors.push(`Error: README.md not found in ${process.cwd()}`);
|
||||
}
|
||||
|
||||
// 3. Check for LICENSE
|
||||
if (!fs.existsSync(licensePath)) {
|
||||
errors.push(`Error: LICENSE file not found in ${process.cwd()}`);
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
console.error('Pre-publish checks failed:');
|
||||
errors.forEach((error) => console.error(`- ${error}`));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log('Pre-publish checks passed.');
|
||||
@@ -1,47 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
const {
|
||||
npm_package_config_sandboxImageUri,
|
||||
DOCKER_DRY_RUN,
|
||||
GEMINI_SANDBOX_IMAGE_TAG,
|
||||
} = process.env;
|
||||
|
||||
if (!npm_package_config_sandboxImageUri) {
|
||||
console.error(
|
||||
'Error: npm_package_config_sandboxImageUri environment variable is not set (should be run via npm).',
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let imageUri = npm_package_config_sandboxImageUri;
|
||||
|
||||
if (GEMINI_SANDBOX_IMAGE_TAG) {
|
||||
const [baseUri] = imageUri.split(':');
|
||||
imageUri = `${baseUri}:${GEMINI_SANDBOX_IMAGE_TAG}`;
|
||||
}
|
||||
|
||||
if (DOCKER_DRY_RUN) {
|
||||
console.log(`DRY RUN: Would execute: docker push "${imageUri}"`);
|
||||
} else {
|
||||
console.log(`Executing: docker push "${imageUri}"`);
|
||||
execSync(`docker push "${imageUri}"`, { stdio: 'inherit' });
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { execSync, spawn } from 'child_process';
|
||||
import yargs from 'yargs';
|
||||
import { hideBin } from 'yargs/helpers';
|
||||
|
||||
try {
|
||||
execSync('node scripts/sandbox_command.js -q');
|
||||
} catch {
|
||||
console.error('ERROR: sandboxing disabled. See docs to enable sandboxing.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const argv = yargs(hideBin(process.argv)).option('i', {
|
||||
alias: 'interactive',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
}).argv;
|
||||
|
||||
if (argv.i && !process.stdin.isTTY) {
|
||||
console.error(
|
||||
'ERROR: interactive mode (-i) requested without a terminal attached',
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const image = 'gemini-cli-sandbox';
|
||||
const sandboxCommand = execSync('node scripts/sandbox_command.js')
|
||||
.toString()
|
||||
.trim();
|
||||
|
||||
const sandboxes = execSync(
|
||||
`${sandboxCommand} ps --filter "ancestor=${image}" --format "{{.Names}}"`,
|
||||
)
|
||||
.toString()
|
||||
.trim()
|
||||
.split('\n')
|
||||
.filter(Boolean);
|
||||
|
||||
let sandboxName;
|
||||
const firstArg = argv._[0];
|
||||
|
||||
if (firstArg) {
|
||||
if (firstArg.startsWith(image) || /^\d+$/.test(firstArg)) {
|
||||
sandboxName = firstArg.startsWith(image)
|
||||
? firstArg
|
||||
: `${image}-${firstArg}`;
|
||||
argv._.shift();
|
||||
}
|
||||
}
|
||||
|
||||
if (!sandboxName) {
|
||||
if (sandboxes.length === 0) {
|
||||
console.error(
|
||||
'No sandboxes found. Are you running gemini-cli with sandboxing enabled?',
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
if (sandboxes.length > 1) {
|
||||
console.error('Multiple sandboxes found:');
|
||||
sandboxes.forEach((s) => console.error(` ${s}`));
|
||||
console.error(
|
||||
'Sandbox name or index (0,1,...) must be specified as first argument',
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
sandboxName = sandboxes[0];
|
||||
}
|
||||
|
||||
if (!sandboxes.includes(sandboxName)) {
|
||||
console.error(`unknown sandbox ${sandboxName}`);
|
||||
console.error('known sandboxes:');
|
||||
sandboxes.forEach((s) => console.error(` ${s}`));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const execArgs = [];
|
||||
let commandToRun = [];
|
||||
|
||||
// Determine interactive flags.
|
||||
// If a command is provided, only be interactive if -i is passed.
|
||||
// If no command is provided, always be interactive.
|
||||
if (argv._.length > 0) {
|
||||
if (argv.i) {
|
||||
execArgs.push('-it');
|
||||
}
|
||||
} else {
|
||||
execArgs.push('-it');
|
||||
}
|
||||
|
||||
// Determine the command to run inside the container.
|
||||
if (argv._.length > 0) {
|
||||
// Join all positional arguments into a single command string.
|
||||
const userCommand = argv._.join(' ');
|
||||
// The container is Linux, so we use bash -l -c to execute the command string.
|
||||
// This is cross-platform because it's what the container runs, not the host.
|
||||
commandToRun = ['bash', '-l', '-c', userCommand];
|
||||
} else {
|
||||
// No command provided, so we start an interactive bash login shell.
|
||||
commandToRun = ['bash', '-l'];
|
||||
}
|
||||
|
||||
const spawnArgs = ['exec', ...execArgs, sandboxName, ...commandToRun];
|
||||
|
||||
// Use spawn to avoid shell injection issues and handle arguments correctly.
|
||||
spawn(sandboxCommand, spawnArgs, { stdio: 'inherit' });
|
||||
@@ -1,42 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
try {
|
||||
execSync('command -v npm', { stdio: 'ignore' });
|
||||
} catch {
|
||||
console.log('npm not found. Installing npm via nvm...');
|
||||
try {
|
||||
execSync(
|
||||
'curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash',
|
||||
{ stdio: 'inherit' },
|
||||
);
|
||||
const nvmsh = `\\. "$HOME/.nvm/nvm.sh"`;
|
||||
execSync(`${nvmsh} && nvm install 22`, { stdio: 'inherit' });
|
||||
execSync(`${nvmsh} && node -v`, { stdio: 'inherit' });
|
||||
execSync(`${nvmsh} && nvm current`, { stdio: 'inherit' });
|
||||
execSync(`${nvmsh} && npm -v`, { stdio: 'inherit' });
|
||||
} catch {
|
||||
console.error('Failed to install nvm or node.');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Development environment setup complete.');
|
||||
Reference in New Issue
Block a user