[{"data":1,"prerenderedAt":464},["ShallowReactive",2],{"\u002Fposts\u002F6cca3ef":3,"surround-\u002Fposts\u002F6cca3ef":453},{"id":4,"title":5,"body":6,"categories":426,"date":428,"description":429,"draft":430,"extension":431,"image":432,"meta":433,"navigation":435,"path":436,"permalink":436,"published":437,"readingTime":438,"recommend":437,"references":437,"seo":443,"sitemap":444,"stem":445,"tags":446,"type":451,"updated":428,"__hash__":452},"content\u002Fposts\u002F2026\u002Ffirstpypi.md","学习日志：开发mac-pkg-history —— 从 pkgutil 到 PyPI 发布",{"type":7,"value":8,"toc":409},"minimark",[9,14,21,24,28,36,43,46,48,52,69,79,85,93,95,99,108,119,138,144,147,150,157,163,166,183,185,189,192,199,203,209,213,221,224,230,236,239,245,248,328,330,334,372,374,377],[10,11,13],"h1",{"id":12},"学习日志mac-pkg-history-从-pkgutil-到-pypi-发布","学习日志：mac-pkg-history —— 从 pkgutil 到 PyPI 发布",[15,16,17],"blockquote",{},[18,19,20],"p",{},"2026-06-28",[22,23],"hr",{},[25,26,27],"h2",{"id":27},"背景",[18,29,30,31,35],{},"在 macOS 上装软件，经常碰到 ",[32,33,34],"code",{"code":34},".pkg"," 安装包。双击 → 下一步 → 下一步 → 安装完成。但它到底往我电脑里放了什么文件？改了哪些地方？—— 我不知道（并感到焦虑，害怕会把电脑文件系统搞得乱糟糟）。",[18,37,38,39,42],{},"调查发现系统自带 ",[32,40,41],{"code":41},"pkgutil"," 命令可查安装记录，于是用 DeepSeek 快速做了个交互友好的小工具，然后发布到了 PyPI 和 GitHub。",[18,44,45],{},"写这篇博客来记录今天的学习。",[22,47],{},[25,49,51],{"id":50},"pkgutil-核心用法","pkgutil 核心用法",[18,53,54,55,57,58,61,62,65,66,68],{},"macOS 每次 ",[32,56,34],{"code":34}," 安装后会留一份收据（receipt，",[32,59,60],{"code":60},".bom"," 格式），存放在 ",[32,63,64],{"code":64},"\u002Fvar\u002Fdb\u002Freceipts\u002F","。",[32,67,41],{"code":41}," 就是用来查询这些收据的。",[70,71,77],"pre",{"className":72,"code":74,"language":75,"meta":76},[73],"language-bash","pkgutil --pkgs                     # 列出所有已安装包的标识符\npkgutil --pkg-info \u003CID>           # 查版本号、install-time（Unix 时间戳）\npkgutil --files \u003CID>              # 列出该包安装的所有文件（相对根目录）\npkgutil --file-info \u002Fpath\u002Ffile    # 反向查：某文件属于哪个包\n","bash","",[32,78,74],{"__ignoreMap":76},[18,80,81,84],{},[32,82,83],{"code":83},"--pkg-info"," 输出示例：",[70,86,91],{"className":87,"code":89,"language":90},[88],"language-text","package-id: org.xquartz.X11\nversion: 2.8.5\nvolume: \u002F\nlocation:\ninstall-time: 1747129510\n","text",[32,92,89],{"__ignoreMap":76},[22,94],{},[25,96,98],{"id":97},"从命令行到工具mac-pkg-history-的诞生","从命令行到工具：mac-pkg-history 的诞生",[18,100,101],{},[102,103,107],"a",{"href":104,"rel":105},"https:\u002F\u002Fgithub.com\u002FYalois\u002Fmac-pkg-history",[106],"nofollow","Yalois\u002Fmac-pkg-history: macOS PKG Install History TUI Viewer.",[18,109,110,111,113,114,118],{},"有了 ",[32,112,41],{"code":41},"，理论上我能查任何包的信息。但是",[115,116,117],"strong",{},"效率有点低","：",[120,121,122,129,132,135],"ul",{},[123,124,125,126],"li",{},"每次要敲完整的 ",[32,127,128],{"code":128},"pkgutil --pkg-info xxx",[123,130,131],{},"包 ID 很长很难记",[123,133,134],{},"文件列表显示刷屏，不便于查看",[123,136,137],{},"想看最近装了什么，得手动排序",[18,139,140,143],{},[115,141,142],{},"我需要一个交互式浏览器。"," 正好这段时间在用 DeepSeek 做 vibe coding，之前也很早想做这种小项目来练手了，于是就立马开始操作了。",[18,145,146],{},"用 DeepSeek 自然语言描述需求 → AI 生成代码 → 跑 → 发现问题 → 描述问题 → AI 改。循环越短效率越高。",[18,148,149],{},"好一次提出大体的框架，就算没有具体思路，可以问Deepseek，让它给出设计方案，然后选择。",[18,151,152,153,156],{},"最终产物：约 150 行 Python，核心依赖只有一个 ",[32,154,155],{"code":155},"rich"," 库。",[70,158,161],{"className":159,"code":160,"language":90},[88],"pip install mac-pkg-history\n",[32,162,160],{"__ignoreMap":76},[18,164,165],{},"以后任何 Mac 用户想查 pkg 安装历史，只需：",[167,168,169,174,180],"ol",{},[123,170,171],{},[32,172,173],{"code":173},"pip install mac-pkg-history",[123,175,176,177],{},"运行 ",[32,178,179],{"code":179},"mac-pkg-history",[123,181,182],{},"在彩色表格里搜索、浏览、查看文件",[22,184],{},[25,186,188],{"id":187},"pypi-发布流程","PyPI 发布流程",[18,190,191],{},"写完代码只是第一步，我的第一个commit只有main.py文件和README文件。",[18,193,194,195,198],{},"让别人能用 ",[32,196,197],{"code":197},"pip install"," 安装才叫「发布」，于是又重新修改了项目结构。",[200,201,202],"h3",{"id":202},"项目结构",[70,204,207],{"className":205,"code":206,"language":90},[88],"mac-pkg-history\u002F\n├── mac_pkg_history\u002F       # 包目录\n│   ├── __init__.py\n│   └── main.py\n├── pyproject.toml          # 打包配置\n├── README.md\n├── LICENSE\n├── .gitignore\n",[32,208,206],{"__ignoreMap":76},[200,210,212],{"id":211},"pyprojecttoml-关键字段","pyproject.toml 关键字段",[70,214,219],{"className":215,"code":217,"language":218,"meta":76},[216],"language-toml","[project]\nname = \"mac-pkg-history\"           # PyPI 全局唯一\nversion = \"0.1.0\"                  # 语义化版本，上传后不可覆盖\nreadme = \"README.md\"               # 会自动渲染为 PyPI 页面\ndescription = \"A TUI tool to view macOS PKG installation history and files\" #描述\nauthors = [\n    { name = \"Yalois\", email = \"yalois.xy@foxmail.com\" },\n]\nclassifiers = [                    # PyPI 分类标签，帮助别人搜索到\n    \"Programming Language :: Python :: 3\",\n    \"License :: OSI Approved :: MIT License\",\n    \"Operating System :: MacOS :: MacOS X\",\n    \"Environment :: Console\",\n]\ndependencies = [\n    \"rich\",                        # 运行时依赖\n]\n\n[project.scripts]\nmac-pkg-history = \"mac_pkg_history.main:main\"\n# 格式：命令名 = \"模块.文件:函数名\"\n\n[project.urls]\nHomepage = \"https:\u002F\u002Fgithub.com\u002FYalois\u002Fmac-pkg-history\"\nRepository = \"https:\u002F\u002Fgithub.com\u002FYalois\u002Fmac-pkg-history\"\nIssues = \"https:\u002F\u002Fgithub.com\u002FYalois\u002Fmac-pkg-history\u002Fissues\"\n","toml",[32,220,217],{"__ignoreMap":76},[200,222,223],{"id":223},"构建和上传",[70,225,228],{"className":226,"code":227,"language":75,"meta":76},[73],"pip install build twine            # 一次性安装工具\n\npython -m build                    # 生成 dist\u002F*.whl + dist\u002F*.tar.gz\ntwine upload dist\u002F*                # 按提示输入 API token\n",[32,229,227],{"__ignoreMap":76},[18,231,232,235],{},[115,233,234],{},"API token"," 在 pypi.org → Account Settings → API tokens 创建，比密码更安全。",[200,237,238],{"id":238},"更新版本后的流程",[70,240,243],{"className":241,"code":242,"language":90},[88],"1. 修改 pyproject.toml 中 version\n2. git commit && git push\n3. git tag v0.2.0 && git push --tags\n4. rm -rf dist\u002F && python -m build\n5. twine upload dist\u002F*\n",[32,244,242],{"__ignoreMap":76},[200,246,247],{"id":247},"常见问题",[249,250,251,267],"table",{},[252,253,254],"thead",{},[255,256,257,261,264],"tr",{},[258,259,260],"th",{},"问题",[258,262,263],{},"原因",[258,265,266],{},"解决",[268,269,270,282,300,314],"tbody",{},[255,271,272,276,279],{},[273,274,275],"td",{},"twine 403",[273,277,278],{},"版本号已存在",[273,280,281],{},"PyPI 不允许覆盖，必须递增版本号",[255,283,284,287,293],{},[273,285,286],{},"pip install 后无命令",[273,288,289,292],{},[32,290,291],{"code":291},"[project.scripts]"," 未配或路径错",[273,294,295,296,299],{},"检查 ",[32,297,298],{"code":298},"模块.文件:函数名"," 格式",[255,301,302,305,308],{},[273,303,304],{},"import 报错",[273,306,307],{},"包目录未被 include",[273,309,310,311],{},"加 ",[32,312,313],{"code":313},"[tool.setuptools.packages.find]",[255,315,316,319,325],{},[273,317,318],{},"缺依赖",[273,320,321,324],{},[32,322,323],{"code":323},"dependencies"," 未声明",[273,326,327],{},"确保所有 runtime 依赖都在此列出",[22,329],{},[25,331,333],{"id":332},"github-项目标配","GitHub 项目标配",[120,335,336,342,348,354],{},[123,337,338,341],{},[115,339,340],{},"README.md","：一句话说明 + 截图 + 安装方式 + 使用方法（表格）+ 原理 + 声明",[123,343,344,347],{},[115,345,346],{},"Badge","：许可证、Python 版本、平台",[123,349,350,353],{},[115,351,352],{},"LICENSE","：MIT 最宽松",[123,355,356,118,359,362,363,362,366,362,369],{},[115,357,358],{},".gitignore",[32,360,361],{"code":361},"__pycache__\u002F","、",[32,364,365],{"code":365},"dist\u002F",[32,367,368],{"code":368},"*.egg-info\u002F",[32,370,371],{"code":371},"build\u002F",[22,373],{},[25,375,376],{"id":376},"收获",[167,378,379,384,390,400,403],{},[123,380,381,383],{},[32,382,41],{"code":41}," 三个核心命令及 receipt 机制",[123,385,386,389],{},[32,387,388],{"code":388},"pyproject.toml"," 完整打包配置",[123,391,392,395,396,399],{},[32,393,394],{"code":394},"python -m build"," → ",[32,397,398],{"code":398},"twine upload"," 发布流程",[123,401,402],{},"DeepSeek vibecoding：AI 搭框架 + 人把控交互和边界",[123,404,405,406,408],{},"把命令行知识封装为 ",[32,407,197],{"code":197}," 可用的工具",{"title":76,"searchDepth":410,"depth":410,"links":411},4,[412,414,415,416,424,425],{"id":27,"depth":413,"text":27},2,{"id":50,"depth":413,"text":51},{"id":97,"depth":413,"text":98},{"id":187,"depth":413,"text":188,"children":417},[418,420,421,422,423],{"id":202,"depth":419,"text":202},3,{"id":211,"depth":419,"text":212},{"id":223,"depth":419,"text":223},{"id":238,"depth":419,"text":238},{"id":247,"depth":419,"text":247},{"id":332,"depth":413,"text":333},{"id":376,"depth":413,"text":376},[427],"代码","2026-06-28 21:34:44","毕业回家第三天，找不到合适的暑期计算机专业相关的工作，在家学习，写写学习日志。",false,"md","https:\u002F\u002Fimg.sky233.top\u002Fimg\u002F2026\u002F06\u002F09af3be103fb875fa2e93f885c27fedc7b6b1afb80ee83cee42c1d13821b69a5.png",{"slots":434},{},true,"\u002Fposts\u002F6cca3ef",null,{"text":439,"minutes":440,"time":441,"words":442},"5 min read",4.98,298800,996,{"title":5,"description":429},{"loc":436},"posts\u002F2026\u002Ffirstpypi",[447,448,449,450],"vibecoding","deepseek","ai开发","python开发","tech","nOXOGCQtmflIQmpt_oFm_KTql6a6oUj2xgbIGjKNOMY",[454,459],{"title":455,"path":456,"stem":457,"date":458,"type":451,"children":-1},"Java反序列化-URLDNS链","\u002Fposts\u002F6a99716","posts\u002F2026\u002FJava反序列化-URLDNS链","2026-05-19 17:14:30",{"title":460,"path":461,"stem":462,"date":463,"type":451,"children":-1},"🚲旧博客内容恢复","\u002F2024","posts\u002F2024\u002F旧博客内容恢复","2024-05-12",1782654304933]