Skip to main content

funpub

多渠道制品发布工具。通过统一接口向不同的制品仓库上传/下载文件,当前支持:

  • aliyun — 阿里云 Packages generic 仓库(协议细节见 docs/aliyun/generic/)

新渠道的接入方式见下方「新增渠道」。

安装

pip install funpub

命令行用法

阿里云账号下可以有多个仓库,同一类型(如 generic)下也可以有多个不同名字 的仓库,因此凭证按 (repo-type, repo-name) 两级区分,一个仓库只需要用 funsecret 配置一次,之后命令行只需要传 --repo-name:

funsecret write https://packages.aliyun.com/api/protocol/{org_id}/generic/{repo} \
  funpub aliyun generic {repo_name} repo_url
funsecret write {username} funpub aliyun generic {repo_name} username
funsecret write {password} funpub aliyun generic {repo_name} password
# 上传(repo-type 默认 generic,凭证从 funsecret 按 repo-name 自动读取)
funpub upload ./dist/app-1.0.0.tar.gz path/to/app \
  --version 1.0.0 --repo-name {repo_name}

# 下载
funpub download path/to/app --version 1.0.0 \
  --repo-name {repo_name} --output ./downloads/

# 生成临时免密下载地址
funpub sign-url path/to/app --version 1.0.0 \
  --repo-name {repo_name} --expiration-seconds 3600

# 查看已注册的渠道
funpub channels

也可以不经 funsecret,直接用 --repo-url/--username/--password 传入:

funpub upload ./dist/app-1.0.0.tar.gz path/to/app \
  --version 1.0.0 \
  --repo-url https://packages.aliyun.com/api/protocol/{org_id}/generic/{repo} \
  --username {username} --password {password}

Python 用法

from funpub import get_publisher

# 方式一:仓库信息已通过 funsecret 配置过,只需要传 repo_name
publisher = get_publisher("aliyun", repo_name="{repo_name}")

# 方式二:直接传 repo_url/username/password,不依赖 funsecret
publisher = get_publisher(
    "aliyun",
    repo_url="https://packages.aliyun.com/api/protocol/{org_id}/generic/{repo}",
    username="...",
    password="...",
)

# 文件大小超过 chunk_threshold(默认等于 chunk_size,100MB)会自动走分块上传
result = publisher.upload_file(
    filepath="./dist/app-1.0.0.tar.gz",
    path="path/to/app",
    version="1.0.0",
)
print(result.url, result.md5)

publisher.download_file(path="path/to/app", version="1.0.0", save_dir="./downloads")
url = publisher.get_download_url(path="path/to/app", version="1.0.0")

设计

  • funpub.core.BasePublisher:所有渠道实现的统一接口 (upload_file / download_file / exist / get_download_url)。
  • funpub.core.PublishResult:上传结果,字典 + 属性双重访问 (path / version / url / size / md5 / sha1 / sha256)。
  • funpub.channels:渠道注册表,懒加载各渠道模块 —— import funpub 不会 拉入任何渠道的第三方依赖,只有真正使用某个渠道时才会导入它。

新增渠道

  1. 在 src/funpub/channels/<name>/ 下实现一个 BasePublisher 子类。
  2. 在 src/funpub/channels/__init__.py 的 CHANNEL_SPECS 里注册一行 ChannelSpec。

开发

uv sync
uv run pytest
uv run ruff check .

关于 farfarfun

farfarfun 是一个专注于实用工具库的开源组织, 涵盖云存储、数据处理、AI、多媒体与开发工具链等方向。

本项目基于 MIT 协议开源。

Metadata

Release files for funpub 0.1.6

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distribution (wheel)

Table of built distributions (wheels) for funpub 0.1.6
File Interpreter ABI Platform
funpub-0.1.6-py3-none-any.whl Python 3 none any Details

Release files / funpub-0.1.6-py3-none-any.whl

Download URL funpub-0.1.6-py3-none-any.whl
Size 19.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6e37476baa8b287dc556b05cbe2b75ff3944eb9c1d28d88009a6a2890aaee283
BLAKE2b-256 checksum
How to use checksums
2ebf991e07e1135c3a757b233667716e2100dd08011368dc87ffafd7b6d16a69
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.17 {"installer":{"name":"uv","version":"0.11.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

This release

0.1.6 This release

1 release file

0.1.5

1 release file

0.1.4

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page