feat(builder): support project-local distribution scripts
This commit is contained in:
@@ -38,13 +38,19 @@ pouch add builder -g # 全局
|
||||
`templates/makefile.builder` 写出项目根 `makefile.builder`,并列出缺的发布配置。
|
||||
不改用户已有的 `Makefile` 或 `.env`。
|
||||
|
||||
配置跟随 `makefile.builder` 所在的 Git 根。如果 APK/IPA 位于 Git submodule,
|
||||
就在 submodule 内配置 `.env.builder` 和 `.pouch/builder/`;父仓库只固定 submodule commit
|
||||
并负责上层编排。
|
||||
|
||||
1. `makefile.builder` 目标:`help/build/clean/version` + 条件 `deb/docker/push*`,
|
||||
变量 `ARCHES/ARCH/VERSION/DIST_DIR/PROJECT_NAME`。
|
||||
`VERSION` 通过 `include <builder>/scripts/version.mk` 从 Git 推导。
|
||||
多架构清单是 `ARCHES`,见下方「多架构」。
|
||||
2. 运行 `python3 -I -S <builder>/scripts/check.py . --ready` 直到构建项 PASS。
|
||||
缺发布键只挡住上传,不挡住构建。
|
||||
3. 在项目根 `.env.builder` 配置发布环境变量(不要写进 `.env`):
|
||||
3. 在项目根 `.env.builder` 配置发布环境变量(不要写进 `.env`)。APK/IPA 可配置
|
||||
`COPY_SCRIPT=.pouch/builder/publish-apk.sh`,它接收产物路径作为位置参数;未配置时使用
|
||||
`COPY_DEST` 的本地目录或容器目录后备逻辑:
|
||||
|
||||
```text
|
||||
DEB_SERVER_URL=https://deb.example.com
|
||||
@@ -53,6 +59,7 @@ DEB_TOKEN=<token> # 只放 .env.builder 或密钥系统,不进 git
|
||||
DOCKER_REGISTRY=registry.example.com
|
||||
COPY_DEST=file-server:/srv
|
||||
COPY_AS=latest.apk
|
||||
# 或:COPY_SCRIPT=.pouch/builder/publish-apk.sh
|
||||
```
|
||||
|
||||
4. 日常发布就是两条命令:
|
||||
@@ -205,7 +212,7 @@ Docker 只有你点名镜像时才走。token 不会写进输出、提交或回
|
||||
| `scripts/upload_deb.sh` | 上传 `.deb` 到 HTTP 包仓库(multipart package/token/repository_name) |
|
||||
| `scripts/publish_docker.sh` | buildx 构建 + 推送镜像,远端 digest 验证 |
|
||||
| `scripts/verify_deb.sh` | 核对包元数据、内容与 SHA-256 |
|
||||
| `scripts/copy_artifact.sh` | 把产物拷到 `COPY_DEST`(本地目录或 `container:/dir`) |
|
||||
| `scripts/copy_artifact.sh` | 按 `COPY_SCRIPT` 分发产物,未配置时拷到 `COPY_DEST` |
|
||||
| `templates/makefile.android.builder` | Android APK + copy 轨道骨架 |
|
||||
| `templates/makefile.ios.builder` | iOS IPA + copy 轨道骨架 |
|
||||
|
||||
|
||||
@@ -10,8 +10,10 @@ description: >-
|
||||
# Builder:DEB / Docker / Android / iOS 构建发布
|
||||
|
||||
**make 管构建,skill 脚本管发布,本 SKILL.md 只留脚本做不了的决策。**
|
||||
项目状态是根目录 `makefile.builder` 与发布用 `.env.builder`,不要创建
|
||||
`.pouch/builder/`,不要改用户的 `Makefile` / `makefile` / `.env`。
|
||||
项目状态是**当前 builder 项目根**的 `makefile.builder` 与 `.env.builder`;项目自定义分发脚本放在
|
||||
同一项目根的 `.pouch/builder/`,不要改用户的 `Makefile` / `makefile` / `.env`。如果父仓库包含
|
||||
submodule,submodule 是独立 builder 项目:它的 `makefile.builder`、`.env.builder` 和
|
||||
`.pouch/builder/` 必须放在 submodule 内,父仓库只负责调用或固定 submodule commit。
|
||||
|
||||
开始时解析当前 `SKILL.md` 所在目录,记为 `<skill-dir>`。优先
|
||||
`git rev-parse --show-toplevel` 解析项目根。
|
||||
@@ -26,7 +28,7 @@ description: >-
|
||||
|
||||
## 初始化
|
||||
|
||||
1. 确认项目根。探测 `makefile.builder`、`.env.builder`、用户 Makefile(只当抄配方的证据,不改)、`Dockerfile`、`debian/`。`.env.builder` 只看键是否存在且非空,不读、不打印值。不要读取用户 `.env`。
|
||||
1. 确认项目根。优先以包含目标 `makefile.builder` 的仓库根为准;若目标位于 submodule,则以 submodule 根为准,不向父仓库回溯配置。探测 `makefile.builder`、`.env.builder`、用户 Makefile(只当抄配方的证据,不改)、`Dockerfile`、`debian/`。`.env.builder` 只看键是否存在且非空,不读、不打印值。不要读取用户 `.env`。
|
||||
2. 判定轨道:有 Dockerfile → docker;有 deb 信号或用户要打 deb → deb;`app.config.ts` / `assembleRelease` / `.apk` → android;`ios/` / `xcodebuild` / `.ipa` → ios;用户要拷到目录或容器 → copy。可多轨道。都不清则问。不要猜测 registry、token、COPY_DEST 或仓库名。
|
||||
3. 没有 `makefile.builder`:按轨道拷模板。deb/docker 用 `templates/makefile.builder`;仅 android 用 `templates/makefile.android.builder`;仅 ios 用 `templates/makefile.ios.builder`。按轨道删掉未使用的 deb/docker/push* 段,把 `build` 的 TODO 换成仓库里已有的真实编译命令(可从用户 Makefile 抄配方,但不要 `include` 或递归调用它),且配方必须使用 `$(ARCH)`。Android/iOS 的 `ARCHES` 是构建机 ISA(fat APK/IPA 仍只留本机一项)。按 [contract.md](references/contract.md) 填 `ARCHES`:`debian/control` / CI matrix / 用户点名的子集;否则只写本机(`x86_64`→`amd64`,`aarch64`→`arm64`)。不要默认双架构;看不出交叉编法时只留一项并说明。`include` builder 的 `scripts/version.mk`。多产物把 `push` 改成 `push: push-deb push-docker push-copy` 的子集。不要改用户 Makefile。
|
||||
4. 已有 `makefile.builder`:跑检查;按 FAIL 给出修补说明。不覆盖 `build`。用户明确要求按契约改时再加 `ARCHES`,并确认配方里有 `$(ARCH)`。不要调用 create-makefile(版本规则冲突)。
|
||||
@@ -50,6 +52,7 @@ DEB_TOKEN=
|
||||
DOCKER_REGISTRY=registry.example.com
|
||||
COPY_DEST=file-server:/srv
|
||||
COPY_AS=latest.apk
|
||||
# 或:COPY_SCRIPT=.pouch/builder/publish-apk.sh
|
||||
```
|
||||
|
||||
## 检查
|
||||
@@ -64,5 +67,5 @@ COPY_AS=latest.apk
|
||||
2. 仅当用户明确要求发布、上传或提交时才上传。不猜测生产端点;不把 token 写入输出、文件、提交或回复;不用 `set -x` 跑含凭据的命令。可能覆盖同版本时先问。脏工作树默认拒绝发布;用户明确接受时设 `ALLOW_UNCOMMITTED=1` 并注明未提交修改。
|
||||
3. 构建:用户指定了架构才传 `ARCH=`;否则不要传,让 makefile 按 `ARCHES` 展开(`make -s -f makefile.builder arches`)。`make -f makefile.builder build` 始终单架构;DEB 用 `make -f makefile.builder deb`。版本按契约从 Git 祖先稳定 tag 推导,不要调用 releaser,不要用全仓库最新 tag。仅获构建授权时不要走会自动上传的目标。脚本含 `rm -rf` 时先确认为受限构建目录。
|
||||
4. DEB 上传前:对每个包 `verify_deb.sh <exact-package-path.deb> <version> <arch>`。多个包时不凭文件时间猜测。镜像轨道由 `publish_docker.sh` 自带远端 inspect。
|
||||
5. 发布优先 `make -f makefile.builder push[-deb|-docker|-copy]`,或直接调 `upload_deb.sh` / `publish_docker.sh` / `copy_artifact.sh`。未指定 `ARCH=` 时 `push-deb` 上传清单里每一个 `.deb`。拷贝轨道要 `COPY_DEST`(本地目录或 `container:/dir`);`COPY_AS` 给第一份文件额外名字。脚本缺环境变量时加载 `.env.builder`(shell 显式值优先),不读 `.env`。不把 token 当命令行参数;不把脚本复制进项目。多平台镜像只能走 `publish_docker.sh`,不能拆进 make;不要把项目架构清单写进 `.env.builder`。`build` 里禁止 `docker cp`。
|
||||
5. 发布优先 `make -f makefile.builder push[-deb|-docker|-copy]`,或直接调 `upload_deb.sh` / `publish_docker.sh` / `copy_artifact.sh`。未指定 `ARCH=` 时 `push-deb` 上传清单里每一个 `.deb`。拷贝轨道至少配置 `COPY_DEST`(本地目录或 `container:/dir`)或 `COPY_SCRIPT`(推荐 `.pouch/builder/` 下的可执行脚本;同时配置时后者优先)。自定义脚本按 `FILE [FILE ...]` 接收产物路径,自行完成重命名、上传、远端校验和通知;相对路径按项目根解析,`BUILDER_PROJECT_ROOT` 提供项目根。`COPY_AS` 只由内置 `COPY_DEST` 路径处理。脚本缺环境变量时加载 `.env.builder`(shell 显式值优先),不读 `.env`。不要把 token 当命令行参数;脚本可随项目放在 `.pouch/builder/` 下。多平台镜像只能走 `publish_docker.sh`,不能拆进 make;不要把项目架构清单写进 `.env.builder`。`build` 里禁止 `docker cp`。
|
||||
6. 发布成功不能只看「curl/push 已执行」。要有退出码、HTTP 成功或远端 digest;索引异步时报告「上传已接受,索引尚待更新」。最终回复给出包名/镜像引用、版本、架构、SHA-256 或 digest、源 commit、工作区状态和未完成项。
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
check.py,本文档跟随。所有接入项目按同一套 make 目标、产物形状和环境变量执行,
|
||||
builder 脚本只做发布,不做项目特定的构建逻辑。
|
||||
|
||||
配置边界按 Git 项目根生效:`makefile.builder` 所在仓库就是 builder 项目根。
|
||||
如果父仓库通过 submodule 引入另一个项目,submodule 的构建、`.env.builder` 和
|
||||
`.pouch/builder/` 分发脚本属于 submodule;父仓库不得替它承载这些配置。
|
||||
|
||||
分工原则:**make 管构建(项目内、确定性),skill 脚本管发布(跨项目 SSOT),
|
||||
Agent 只保留授权判断和歧义处理。**
|
||||
|
||||
@@ -41,7 +45,7 @@ make -f makefile.builder <target>
|
||||
| `arches` | 可选(新模板必备) | 打印一行空格分隔的 `ARCHES`;无此目标视为存量 makefile,隐式单架构 |
|
||||
| `push-deb` | 同时有多种发布轨道的项目 | 仅调 builder 的 `upload_deb.sh`。未指定 `ARCH=` 时上传 `ARCHES` 中每一项对应的 `.deb`;指定了则只上传那一个 |
|
||||
| `push-docker` | 同时有多种发布轨道的项目 | 仅调 builder 的 `publish_docker.sh` |
|
||||
| `push-copy` | 同时有多种发布轨道的项目 | 仅调 builder 的 `copy_artifact.sh` |
|
||||
| `push-copy` | 同时有多种发布轨道的项目 | 仅调 builder 的 `copy_artifact.sh`;由它按配置选择内置拷贝或自定义脚本 |
|
||||
| `push` | 单一产物类型时必备;多产物项目为聚合 | 依序调用对应 push-* 或直接调脚本;是发布的唯一 make 入口 |
|
||||
|
||||
规则:
|
||||
@@ -49,7 +53,8 @@ make -f makefile.builder <target>
|
||||
1. 项目有 DEB 产物的判据:`makefile.builder` 配方引用 `dpkg-deb`/`debuild` 或产出 `.deb`。
|
||||
有镜像的判据:项目根存在 `Dockerfile`。
|
||||
Android 判据:配方产出 `.apk` / `.aab`。iOS 判据:配方产出 `.ipa`。
|
||||
拷贝发布判据:配方调用 `copy_artifact.sh`。
|
||||
拷贝发布判据:配方调用 `copy_artifact.sh`。`copy_artifact.sh` 是稳定入口,具体分发可由
|
||||
`COPY_SCRIPT` 接管。
|
||||
2. 多于一种发布轨道必须拆 `push-deb` / `push-docker` / `push-copy`,`push` 依序聚合;
|
||||
单轨道项目一个 `push` 即可。
|
||||
3. `docker` 目标只能本地构建。多平台镜像无法拆成"make 构建 + 单独推送"
|
||||
@@ -139,10 +144,25 @@ include $(HOME)/.pouch/skills/builder/scripts/version.mk
|
||||
|
||||
| 变量 | 必填 | 说明 |
|
||||
|---|---|---|
|
||||
| `COPY_DEST` | 是 | 本地目录,或 Docker 目标 `container:/absolute/dir`(`docker cp`) |
|
||||
| `COPY_DEST` | 与 `COPY_SCRIPT` 至少配置一个 | 内置后备目标:本地目录,或 Docker 目标 `container:/absolute/dir`(`docker cp`) |
|
||||
| `COPY_SCRIPT` | 与 `COPY_DEST` 至少配置一个 | 推荐 `.pouch/builder/` 下的项目相对路径,也可用绝对路径;必须是可执行文件,同时配置时优先于 `COPY_DEST` |
|
||||
| `COPY_AS` | 否 | 第一份文件的额外 basename,逗号或空格分隔 |
|
||||
|
||||
`copy_artifact.sh FILE [FILE ...]` 把已构建文件拷到 `COPY_DEST`,校验目标存在后打印 SHA-256。不读 `.env`。脏工作树默认拒绝,`ALLOW_UNCOMMITTED=1` 放行。
|
||||
`copy_artifact.sh FILE [FILE ...]` 是 APK/IPA 等产物的统一分发入口。不读 `.env`,脏工作树默认拒绝,
|
||||
`ALLOW_UNCOMMITTED=1` 放行。
|
||||
|
||||
未配置 `COPY_SCRIPT` 时,入口把文件拷到 `COPY_DEST`,校验目标存在后打印 SHA-256。
|
||||
配置 `COPY_SCRIPT=.pouch/builder/publish-apk.sh` 时,入口按项目根解析、先校验每个文件存在,然后直接执行:
|
||||
|
||||
```text
|
||||
/absolute/path/to/publish-script FILE [FILE ...]
|
||||
```
|
||||
|
||||
相对路径只允许位于项目的 `.pouch/builder/` 下;绝对路径仍支持外部脚本。不经过 shell 字符串求值,
|
||||
不把 token 放进参数。脚本通过位置参数接收原始产物绝对路径,自行负责重命名、上传、远端校验和通知;
|
||||
当前项目根以 `BUILDER_PROJECT_ROOT` 环境变量提供。
|
||||
脚本返回非零即视为分发失败;成功后入口只报告脚本成功和本地 SHA-256,不宣称远端已校验。
|
||||
`--dry-run` 只打印将调用的脚本与文件,不执行自定义脚本。
|
||||
|
||||
### Docker 轨道
|
||||
|
||||
|
||||
@@ -442,7 +442,7 @@ def check_secrets_and_tags(report: Report, project: Path) -> None:
|
||||
|
||||
DEB_ENV_KEYS = ("DEB_SERVER_URL", "DEB_TOKEN", "DEB_REPOSITORY")
|
||||
DOCKER_ENV_KEYS = ("DOCKER_REGISTRY",)
|
||||
COPY_ENV_KEYS = ("COPY_DEST",)
|
||||
COPY_ENV_KEYS = ("COPY_DEST", "COPY_SCRIPT")
|
||||
ENV_KEY_LINE = re.compile(r"^([A-Za-z_][A-Za-z0-9_]*)=(.*)$")
|
||||
|
||||
|
||||
@@ -469,10 +469,72 @@ def env_file_keys(project: Path) -> set[str]:
|
||||
return keys
|
||||
|
||||
|
||||
def env_file_values(project: Path) -> dict[str, str]:
|
||||
"""Return nonempty `.env.builder` values for internal validation only."""
|
||||
path = project / BUILDER_ENV
|
||||
values: dict[str, str] = {}
|
||||
if not path.is_file():
|
||||
return values
|
||||
try:
|
||||
text = path.read_text(encoding="utf-8")
|
||||
except OSError:
|
||||
return values
|
||||
for raw in text.splitlines():
|
||||
stripped = raw.strip()
|
||||
if not stripped or stripped.startswith("#"):
|
||||
continue
|
||||
match = ENV_KEY_LINE.match(stripped)
|
||||
if not match:
|
||||
continue
|
||||
value = match.group(2).strip().strip("'\"")
|
||||
if value:
|
||||
values[match.group(1)] = value
|
||||
return values
|
||||
|
||||
|
||||
def key_present(key: str, env_keys: set[str]) -> bool:
|
||||
return bool(os.environ.get(key)) or key in env_keys
|
||||
|
||||
|
||||
def configured_value(key: str, env_values: dict[str, str]) -> str:
|
||||
"""Read a configured value without ever printing it."""
|
||||
return os.environ.get(key, "") or env_values.get(key, "")
|
||||
|
||||
|
||||
def resolve_copy_script(project: Path, script: str) -> tuple[Path | None, str | None]:
|
||||
"""Resolve COPY_SCRIPT without allowing arbitrary project-relative paths."""
|
||||
configured = Path(script).expanduser()
|
||||
if configured.is_absolute():
|
||||
return configured, None
|
||||
try:
|
||||
resolved = (project / configured).resolve()
|
||||
allowed_root = (project / ".pouch" / "builder").resolve()
|
||||
resolved.relative_to(allowed_root)
|
||||
except ValueError:
|
||||
return None, "relative COPY_SCRIPT must be under .pouch/builder/"
|
||||
return resolved, None
|
||||
|
||||
|
||||
def check_copy_script(report: Report, project: Path, env_values: dict[str, str]) -> None:
|
||||
script = configured_value("COPY_SCRIPT", env_values)
|
||||
if not script:
|
||||
return
|
||||
path, resolve_error = resolve_copy_script(project, script)
|
||||
problems = []
|
||||
if resolve_error:
|
||||
problems.append(resolve_error)
|
||||
elif path is not None and not path.is_file():
|
||||
problems.append("COPY_SCRIPT does not point to a file")
|
||||
elif path is not None and not os.access(path, os.X_OK):
|
||||
problems.append("COPY_SCRIPT is not executable")
|
||||
report.add(
|
||||
FAIL if problems else PASS,
|
||||
14,
|
||||
"COPY_SCRIPT 可执行且路径明确",
|
||||
"\n".join(problems) if problems else "configured custom copy script is executable",
|
||||
)
|
||||
|
||||
|
||||
def check_ready_toolchain(
|
||||
report: Report, *, deb_project: bool, docker_project: bool, copy_project: bool = False
|
||||
) -> None:
|
||||
@@ -516,6 +578,7 @@ def check_ready_env_keys(
|
||||
copy_project: bool = False,
|
||||
) -> None:
|
||||
env_keys = env_file_keys(project)
|
||||
env_values = env_file_values(project)
|
||||
lines = []
|
||||
missing: list[str] = []
|
||||
if not deb_project and not docker_project and not copy_project:
|
||||
@@ -541,16 +604,24 @@ def check_ready_env_keys(
|
||||
for key in COPY_ENV_KEYS:
|
||||
found = key_present(key, env_keys)
|
||||
lines.append(f"{key}: {'present' if found else 'MISSING'}")
|
||||
if not found:
|
||||
missing.append(key)
|
||||
if not any(key_present(key, env_keys) for key in COPY_ENV_KEYS):
|
||||
missing.append("COPY_DEST or COPY_SCRIPT")
|
||||
check_copy_script(report, project, env_values)
|
||||
else:
|
||||
lines.append("COPY_*: skipped (no copy track)")
|
||||
if missing:
|
||||
if "COPY_DEST or COPY_SCRIPT" in missing:
|
||||
config_hints = [
|
||||
" COPY_DEST= # local/container destination, or",
|
||||
" COPY_SCRIPT=.pouch/builder/publish-apk.sh # choose one",
|
||||
]
|
||||
else:
|
||||
config_hints = [f" {key}=" for key in missing]
|
||||
lines.extend(
|
||||
[
|
||||
"",
|
||||
f"blocks publish, not build. Put keys in the environment or {BUILDER_ENV}:",
|
||||
*[f" {key}=" for key in missing],
|
||||
*config_hints,
|
||||
f"Do not commit {BUILDER_ENV}. Do not put these keys in `.env`. Never print values.",
|
||||
]
|
||||
)
|
||||
|
||||
@@ -6,12 +6,17 @@ usage() {
|
||||
Usage:
|
||||
COPY_DEST=/path/or/container:/dir \
|
||||
copy_artifact.sh FILE [FILE ...]
|
||||
COPY_SCRIPT=.pouch/builder/publish-apk.sh \
|
||||
copy_artifact.sh FILE [FILE ...]
|
||||
|
||||
Copy built artifacts to COPY_DEST. Dest is a local directory, or a Docker
|
||||
copy target `container:/absolute/dir` (docker cp). Does not read `.env`.
|
||||
Copy built artifacts using COPY_SCRIPT when configured; otherwise copy to
|
||||
COPY_DEST. COPY_DEST is a local directory or a Docker copy target
|
||||
`container:/absolute/dir` (docker cp). Does not read `.env`.
|
||||
|
||||
Environment (project root .env.builder; shell values win):
|
||||
COPY_DEST Required. Local dir or container:/dir
|
||||
COPY_DEST Optional fallback. Local dir or container:/dir
|
||||
COPY_SCRIPT Optional .pouch/builder/ relative path or absolute
|
||||
executable. Receives FILE paths as positional args.
|
||||
COPY_AS Optional. Extra basenames for the first file
|
||||
(comma or space separated)
|
||||
ALLOW_UNCOMMITTED=1 Publish despite a dirty working tree
|
||||
@@ -58,9 +63,13 @@ while (($#)); do
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$dest" || $# -eq 0 ]]; then
|
||||
echo "Error: COPY_DEST and at least one file are required." >&2
|
||||
echo "Set them in the environment or the project root .env.builder." >&2
|
||||
if [[ -z "$dest" && -z "${COPY_SCRIPT:-}" || $# -eq 0 ]]; then
|
||||
if [[ $# -eq 0 ]]; then
|
||||
echo "Error: at least one file is required." >&2
|
||||
else
|
||||
echo "Error: set COPY_DEST or COPY_SCRIPT." >&2
|
||||
fi
|
||||
echo "Set the value in the environment or the project root .env.builder." >&2
|
||||
usage >&2
|
||||
exit 2
|
||||
fi
|
||||
@@ -73,6 +82,63 @@ if [[ -n "$project_root" ]] && git -C "$project_root" rev-parse HEAD >/dev/null
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "${COPY_SCRIPT:-}" ]]; then
|
||||
copy_script=$COPY_SCRIPT
|
||||
if [[ "$copy_script" != /* ]]; then
|
||||
relative_script=${copy_script#./}
|
||||
if [[ "$relative_script" != .pouch/builder/* || -z "$project_root" ]]; then
|
||||
echo "Error: relative COPY_SCRIPT must be under .pouch/builder/." >&2
|
||||
exit 2
|
||||
fi
|
||||
copy_script="$project_root/$relative_script"
|
||||
fi
|
||||
if [[ ! -f "$copy_script" ]]; then
|
||||
echo "Error: COPY_SCRIPT is not a file: $copy_script" >&2
|
||||
exit 2
|
||||
fi
|
||||
if [[ ! -x "$copy_script" ]]; then
|
||||
echo "Error: COPY_SCRIPT is not executable: $copy_script" >&2
|
||||
exit 2
|
||||
fi
|
||||
artifact_args=()
|
||||
for src in "$@"; do
|
||||
if [[ ! -f "$src" ]]; then
|
||||
echo "Error: not a file: $src" >&2
|
||||
exit 2
|
||||
fi
|
||||
if [[ "$src" == /* ]]; then
|
||||
artifact_args+=("$src")
|
||||
else
|
||||
artifact_args+=("$PWD/$src")
|
||||
fi
|
||||
done
|
||||
if $dry_run; then
|
||||
printf 'custom copy script:'
|
||||
printf ' %q' "$copy_script" "${artifact_args[@]}"
|
||||
printf '\n'
|
||||
exit 0
|
||||
fi
|
||||
export BUILDER_PROJECT_ROOT=${project_root:-}
|
||||
if "$copy_script" "${artifact_args[@]}"; then
|
||||
if command -v sha256sum >/dev/null 2>&1; then
|
||||
for src in "${artifact_args[@]}"; do
|
||||
sha256sum -- "$src"
|
||||
done
|
||||
fi
|
||||
echo "copy_artifact: custom script completed for $# file(s)"
|
||||
exit 0
|
||||
else
|
||||
status=$?
|
||||
echo "Error: COPY_SCRIPT failed with exit code $status." >&2
|
||||
exit "$status"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -z "$dest" ]]; then
|
||||
echo "Error: COPY_DEST is required when COPY_SCRIPT is not configured." >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
is_docker_dest() {
|
||||
local d=$1
|
||||
[[ "$d" != /* && "$d" != ./* && "$d" != ../* && "$d" != ~* ]] \
|
||||
|
||||
@@ -11,5 +11,6 @@
|
||||
# DOCKER_REPOSITORY=
|
||||
# IMAGE_TAG=
|
||||
# PLATFORMS= # optional override; project ARCHES lives in makefile.builder
|
||||
# COPY_DEST= # local dir, or docker dest container:/absolute/dir
|
||||
# COPY_DEST= # fallback: local dir, or docker dest container:/absolute/dir
|
||||
# COPY_SCRIPT=.pouch/builder/publish-apk.sh # project-local executable; overrides COPY_DEST
|
||||
# COPY_AS= # optional extra basenames for the first file
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# Builder 契约文件。放到项目根,用 `make -f makefile.builder <target>`。
|
||||
# Android 轨道:build 产出 dist/<name>_<version>_<arch>.apk,push 走 copy_artifact.sh。
|
||||
# copy_artifact.sh 会按 COPY_SCRIPT 分发;未配置时回退到 COPY_DEST。
|
||||
# ARCHES 是构建机 ISA;APK 可含多 ABI。不要把这些目标写进用户 Makefile。
|
||||
|
||||
ARCHES ?= amd64
|
||||
@@ -35,7 +36,7 @@ help:
|
||||
@echo " make -f makefile.builder version - 打印规范版本"
|
||||
@echo " make -f makefile.builder arches - 打印架构清单"
|
||||
@echo " make -f makefile.builder clean - 删除 $(DIST_DIR)"
|
||||
@echo " make -f makefile.builder push - copy_artifact.sh 拷到 COPY_DEST"
|
||||
@echo " make -f makefile.builder push - copy_artifact.sh 分发 APK"
|
||||
|
||||
version:
|
||||
@echo $(VERSION)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# Builder 契约文件。放到项目根,用 `make -f makefile.builder <target>`。
|
||||
# iOS 轨道:build 产出 dist/<name>_<version>_<arch>.ipa,push 走 copy_artifact.sh。
|
||||
# copy_artifact.sh 会按 COPY_SCRIPT 分发;未配置时回退到 COPY_DEST。
|
||||
# ARCHES 是构建机 ISA。不要把这些目标写进用户 Makefile。
|
||||
|
||||
ARCHES ?= arm64
|
||||
@@ -35,7 +36,7 @@ help:
|
||||
@echo " make -f makefile.builder version - 打印规范版本"
|
||||
@echo " make -f makefile.builder arches - 打印架构清单"
|
||||
@echo " make -f makefile.builder clean - 删除 $(DIST_DIR)"
|
||||
@echo " make -f makefile.builder push - copy_artifact.sh 拷到 COPY_DEST"
|
||||
@echo " make -f makefile.builder push - copy_artifact.sh 分发 IPA"
|
||||
|
||||
version:
|
||||
@echo $(VERSION)
|
||||
|
||||
Reference in New Issue
Block a user