返回 CodeWhale
publish-crates.sh
根目录 / scripts / release / publish-crates.sh
1 #!/usr/bin/env bash
2 set -euo pipefail
3
4 script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5 # shellcheck source=scripts/release/crates.sh
6 source "${script_dir}/crates.sh"
7
8 mode="${1:-dry-run}"
9 case "${mode}" in
10 dry-run|publish) ;;
11 *)
12 echo "usage: $0 [dry-run|publish]" >&2
13 exit 1
14 ;;
15 esac
16
17 if [[ "${mode}" == "publish" ]]; then
18 "${script_dir}/require-release-tag-checkout.sh"
19 "${script_dir}/verify-release-assets.sh"
20 fi
21
22 packages=("${release_crates[@]}")
23 crates_user_agent="CodeWhale release publish check (https://github.com/Hmbown/CodeWhale)"
24
25 workspace_version=""
26 workspace_codewhale_packages=()
27 workspace_package_dep_flags=()
28
29 while IFS=$'\t' read -r kind name value; do
30 case "${kind}" in
31 version)
32 workspace_version="${name}"
33 ;;
34 crate)
35 workspace_codewhale_packages+=("${name}")
36 workspace_package_dep_flags+=("${value}")
37 ;;
38 esac
39 done < <(
40 python3 - <<'PY'
41 import json
42 import subprocess
43
44 metadata = json.loads(
45 subprocess.check_output(["cargo", "metadata", "--format-version", "1", "--no-deps"])
46 )
47 workspace_members = set(metadata["workspace_members"])
48 workspace_packages = [
49 pkg for pkg in metadata["packages"] if pkg["id"] in workspace_members
50 ]
51 workspace_by_name = {pkg["name"]: pkg for pkg in workspace_packages}
52
53 versions = sorted({pkg["version"] for pkg in workspace_packages})
54 if not versions:
55 raise SystemExit("workspace has no packages")
56 if len(versions) != 1:
57 raise SystemExit(f"workspace packages have mixed versions: {', '.join(versions)}")
58 print(f"version\t{versions[0]}\t")
59
60 for pkg in sorted(workspace_packages, key=lambda item: item["name"]):
61 if not pkg["name"].startswith("codewhale-"):
62 continue
63 has_workspace_dep = any(
64 dep.get("path") and dep["name"] in workspace_by_name
65 for dep in pkg["dependencies"]
66 )
67 print(f"crate\t{pkg['name']}\t{1 if has_workspace_dep else 0}")
68 PY
69 )
70
71 if [[ -z "${workspace_version}" ]]; then
72 echo "Could not determine workspace version." >&2
73 exit 1
74 fi
75
76 missing_packages=()
77 for workspace_package in "${workspace_codewhale_packages[@]}"; do
78 found=0
79 for package in "${packages[@]}"; do
80 if [[ "${package}" == "${workspace_package}" ]]; then
81 found=1
82 break
83 fi
84 done
85 if [[ "${found}" == "0" ]]; then
86 missing_packages+=("${workspace_package}")
87 fi
88 done
89
90 extra_packages=()
91 for package in "${packages[@]}"; do
92 found=0
93 for workspace_package in "${workspace_codewhale_packages[@]}"; do
94 if [[ "${package}" == "${workspace_package}" ]]; then
95 found=1
96 break
97 fi
98 done
99 if [[ "${found}" == "0" ]]; then
100 extra_packages+=("${package}")
101 fi
102 done
103
104 if (( ${#missing_packages[@]} > 0 || ${#extra_packages[@]} > 0 )); then
105 if (( ${#missing_packages[@]} > 0 )); then
106 echo "publish package list is missing workspace crates: ${missing_packages[*]}" >&2
107 fi
108 if (( ${#extra_packages[@]} > 0 )); then
109 echo "publish package list contains non-workspace crates: ${extra_packages[*]}" >&2
110 fi
111 exit 1
112 fi
113
114 package_has_workspace_deps() {
115 local package_name="$1"
116 local index
117 for ((index = 0; index < ${#workspace_codewhale_packages[@]}; index += 1)); do
118 if [[ "${workspace_codewhale_packages[$index]}" == "${package_name}" ]]; then
119 [[ "${workspace_package_dep_flags[$index]}" == "1" ]]
120 return
121 fi
122 done
123
124 echo "Unknown workspace crate: ${package_name}" >&2
125 return 1
126 }
127
128 crate_version_exists() {
129 local crate_name="$1"
130 local crate_version="$2"
131 curl -fsSL -A "${crates_user_agent}" "https://crates.io/api/v1/crates/${crate_name}/${crate_version}" >/dev/null 2>&1
132 }
133
134 wait_for_crate_version() {
135 local crate_name="$1"
136 local crate_version="$2"
137 local attempts=30
138
139 for ((attempt = 1; attempt <= attempts; attempt += 1)); do
140 if crate_version_exists "${crate_name}" "${crate_version}"; then
141 return 0
142 fi
143 echo "Waiting for ${crate_name} ${crate_version} to appear on crates.io (${attempt}/${attempts})..."
144 sleep 10
145 done
146
147 echo "Timed out waiting for ${crate_name} ${crate_version} to appear on crates.io" >&2
148 return 1
149 }
150
151 for package in "${packages[@]}"; do
152 echo "::group::${mode} ${package}"
153 if [[ "${mode}" == "dry-run" ]]; then
154 if package_has_workspace_deps "${package}"; then
155 cargo package --allow-dirty --locked --list -p "${package}" >/dev/null
156 echo "Verified package contents for ${package}; full crates.io dry-run requires workspace dependencies at ${workspace_version} to be published first."
157 else
158 cargo publish --dry-run --locked --allow-dirty -p "${package}"
159 fi
160 else
161 if crate_version_exists "${package}" "${workspace_version}"; then
162 echo "Skipping ${package} ${workspace_version}; already published."
163 else
164 cargo publish --locked -p "${package}"
165 wait_for_crate_version "${package}" "${workspace_version}"
166 fi
167 fi
168 echo "::endgroup::"
169 done
170
170 lines BASH