npm + PyPI 双平台发布实战:从零到自动化的完整踩坑记录

📅 2026/7/11 6:04:08
npm + PyPI 双平台发布实战:从零到自动化的完整踩坑记录
两个项目、两个平台、一个下午。npm 的 Granular Token、PyPI 的 twine 编码坑、GitHub Actions 的 CI/CD 管线——全部走通后的完整记录。背景最近做了两个开源项目项目语言类型发布平台GEO CopilotJavaScriptChrome 扩展npmLAN TransferPython命令行工具PyPI两个都是零依赖的轻量项目但发布流程走了不少弯路。这篇文章把两条路线一次性讲清楚下次照着走15 分钟一个。第一部分npm 发布GEO Copilot项目准备GEO Copilot 是 Chrome 扩展纯 HTMLCSSJS零依赖。需要新增✅ package.json # npm 包配置 ✅ package-lock.json # 依赖锁空包也要 ✅ test/basic.test.js # 基础测试 ✅ .github/workflows/npm-publish.yml ✅ .gitattributes # 统一换行符package.json 关键配置{name:aicdragon/geo-copilot,version:0.1.0,description:GEO workflow browser extension,files:[manifest.json,src/,assets/,README.md],scripts:{test:node test/basic.test.js},publishConfig:{access:public}}踩坑包名geo-copilot被 npm 拒绝和已有geocopilot太相似改用aicdragon/geo-copilot。CI/CD 配置name:Node.js Packageon:release:types:[created]jobs:build:runs-on:ubuntu-lateststeps:-uses:actions/checkoutv4-uses:actions/setup-nodev4with:{node-version:20}-run:npm install# 注意用 install 不用 ci-run:npm testpublish-npm:needs:buildruns-on:ubuntu-lateststeps:-uses:actions/checkoutv4-uses:actions/setup-nodev4with:node-version:20registry-url:https://registry.npmjs.org/-run:npm install-run:npm publish--access publicenv:NODE_AUTH_TOKEN:${{secrets.npm_token}}npm Token — 2026 版全坑记录2025 年 11 月起npm 只支持Granular Access TokenClassic Token 取消。正确流程npmjs.com → 头像 → Access Tokens →Granular Access Token必选Packages and scopes →All Packages下拉里有不是搜索必选Permissions →Read and write必勾✅Bypass two-factor authentication没勾 403GitHub Secrets → 新建npm_tokennpm 报错速查报错原因解决401 UnauthorizedToken 无效重新生成403 2FA required没勾 Bypass 2FA重新生成勾上404 Not Found包名冲突/无权限改用用户名/包名403 too similar包名和已有包太像加作用域前缀CInpm ci挂lockfile 版本不兼容改用npm installRe-run 失败用旧 commit 重跑删 tag 重建 Release第二部分PyPI 发布LAN Transfer项目准备LAN Transfer 是 Python 工具零依赖纯标准库。需要新增✅ pyproject.toml # 现代 Python 打包标准 ✅ test/test_transfer.py # 导入测试 ✅ .github/workflows/pypi-publish.yml ✅ .gitattributes # 统一换行符pyproject.toml 关键配置[build-system] requires [setuptools64, wheel] build-backend setuptools.build_meta [project] name lan-transfer version 0.1.0 description LAN AirDrop — zero-dependency network file transfer tool requires-python 3.8 dependencies [] # 零第三方依赖 [project.scripts] lan-transfer lan_transfer:main lan-transfer-gui lan_transfer_gui:main踩坑build-backend别写setuptools.backends._legacy——新版本里改名了直接用setuptools.build_meta。CI/CD 配置name:Python Packageon:release:types:[created]jobs:build:runs-on:ubuntu-lateststeps:-uses:actions/checkoutv4-uses:actions/setup-pythonv5with:{python-version:3.12}-run:pip install build-run:python-m build-run:pip install dist/*.whl-run:python test/test_transfer.pypublish-pypi:needs:buildruns-on:ubuntu-lateststeps:-uses:actions/checkoutv4-uses:actions/setup-pythonv5with:{python-version:3.12}-run:pip install build-run:python-m build-uses:pypa/gh-action-pypi-publishrelease/v1with:password:${{secrets.PYPI_TOKEN}}PyPI Token和 npm 不同PyPI 的 Token 生成简单得多pypi.org → Account Settings → API tokens → Add API tokenScopeEntire account生成后复制 → GitHub Secrets →PYPI_TOKENtwine 上传踩坑Windows 终端编码导致 twine 进度条崩溃UnicodeEncodeError: gbk codec cant encode character \u2022解决加--disable-progress-bar并设置PYTHONUTF81。$env:PYTHONUTF8 1python-m twine upload--disable-progress-bar dist/*dist 目录注意PyInstaller 打包生成的.exe也在dist/里twine 会报InvalidDistribution。只上传.whl和.tar.gz。第三部分双平台对比npmPyPI配置文件package.jsonpyproject.toml构建命令npm installpython -m build测试命令npm testpython test/xxx.pyToken 类型Granular2026强制API Token简单Token 复杂度⭐⭐⭐⭐⭐5 步选⭐1 步2FA 问题必须勾 Bypass默认不需要包名冲突加scope解决直接换名发布命令npm publishtwine upload dist/*CI 发布setup-nodepypa/gh-action-pypi-publish安装命令npm i scope/pkgpip install pkgGitHub Secretnpm_tokenPYPI_TOKEN结论PyPI 的发布体验比 npm 顺滑太多了——npm 2026 年的 Granular Token 流程真的过于复杂。第四部分统一速查清单npm 发布□ 1. package.json 有 name/scope、scripts.test、publishConfig、files □ 2. npm install → package-lock.json 生成 □ 3. npm test 本地通过 □ 4. .github/workflows/npm-publish.yml 配置完成 □ 5. npmjs.com → Granular Token → All Packages → ReadWrite → Bypass 2FA □ 6. GitHub Secrets → npm_token 已存入 □ 7. Git push → GitHub Release → 等待 Actions 绿灯PyPI 发布□ 1. pyproject.toml 有 name、version、scripts、build-system □ 2. python -m build → 生成 .whl .tar.gz □ 3. python test/ 测试通过 □ 4. .github/workflows/pypi-publish.yml 配置完成 □ 5. pypi.org → API Token → Entire account □ 6. GitHub Secrets → PYPI_TOKEN 已存入 □ 7. Git push → GitHub Release → 等待 Actions 绿灯两个都需要的通用配置□ .gitattributes 存在统一 LF 换行符 □ GitHub Release 触发 CIRe-run 用旧 commit需删 tag 重建 □ 首次发布建议本地手动跑一次排查命名冲突和权限问题五、本次成功案例项目平台包名链接GEO Copilotnpmaicdragon/geo-copilotnpmjs.comLAN TransferPyPIlan-transferpypi.org两个项目从零到双平台发布总共踩了这些坑npm Granular Token 必须选 All Packages Bypass 2FAnpm 包名冲突加scope前缀GitHub Actionsnpm ci改用npm installRe-run 用的是旧 commit需删 tag 重建 ReleasePyPI twine 在 Windows 终端编码崩溃dist/里有 PyInstaller 的 exetwine 报 InvalidDistributionbuild-backend老教程写的是已废弃的_legacy希望这份记录能帮你下次少踩这些坑。 相关资源npmnpmjs.com/package/aicdragon/geo-copilotPyPIpypi.org/project/lan-transferGitHubCDragon123-code/geo-copilot | CDragon123-code/lan-file-transfer