返回 DeepSeek-Reasonix
resolve-desktop-candidate.sh
根目录 / scripts / resolve-desktop-candidate.sh
1 #!/usr/bin/env bash
2 # Resolve and authorize the immutable source commit for a Desktop publication.
3 # The workflow control plane must run from protected main-v2. Stable/RC builds
4 # use an existing tag on main-v2 history; Preview builds use the exact main-v2
5 # commit selected when the protected workflow was dispatched.
6 set -euo pipefail
7
8 channel="${RELEASE_CHANNEL:?RELEASE_CHANNEL is required}"
9 tag="${RELEASE_TAG:?RELEASE_TAG is required}"
10 orchestrated="${IN_ORCHESTRATED:-false}"
11 orchestrator="${IN_ORCHESTRATOR:-}"
12 candidate_preparation="${CANDIDATE_PREPARATION:-false}"
13 candidate_rehearsal="${CANDIDATE_REHEARSAL:-false}"
14 approved_sha="${APPROVED_SHA:-}"
15 caller_event="${CALLER_EVENT_NAME:-}"
16 caller_ref="${CALLER_REF:-}"
17 caller_ref_protected="${CALLER_REF_PROTECTED:-}"
18 caller_sha="${CALLER_SHA:-}"
19 caller_workflow_sha="${CALLER_WORKFLOW_SHA:-}"
20 release_remote="${RELEASE_REMOTE:-origin}"
21 require_current_main="${REQUIRE_CURRENT_MAIN:-true}"
22 verify_checkout="${VERIFY_RELEASE_CHECKOUT:-false}"
23
24 case "$orchestrated" in
25 true | false) ;;
26 *)
27 echo "::error::IN_ORCHESTRATED must be true or false, got: $orchestrated" >&2
28 exit 2
29 ;;
30 esac
31 if [ "$orchestrated" = "true" ]; then
32 case "$orchestrator" in
33 stable | preview | candidate | promote) ;;
34 *)
35 echo "::error::IN_ORCHESTRATOR must be stable or preview for an orchestrated Desktop release, got: $orchestrator" >&2
36 exit 2
37 ;;
38 esac
39 if [ "$orchestrator" != "$channel" ] && [ "$orchestrator" != candidate ] && [ "$orchestrator" != promote ]; then
40 echo "::error::the $orchestrator orchestrator cannot authorize a Desktop $channel candidate" >&2
41 exit 1
42 fi
43 fi
44 case "$candidate_preparation" in
45 true | false) ;;
46 *)
47 echo "::error::CANDIDATE_PREPARATION must be true or false, got: $candidate_preparation" >&2
48 exit 2
49 ;;
50 esac
51 if [ "$candidate_preparation" = true ] && { [ "$orchestrated" != true ] || [ "$orchestrator" != candidate ] || [ "$channel" != stable ]; }; then
52 echo "::error::candidate preparation requires the protected candidate orchestrator" >&2
53 exit 1
54 fi
55 case "$candidate_rehearsal" in
56 true)
57 [ "$candidate_preparation" = true ] || { echo "::error::rehearsal requires non-publishing candidate preparation" >&2; exit 1; }
58 ;;
59 false) ;;
60 *) echo "::error::CANDIDATE_REHEARSAL must be true or false" >&2; exit 2 ;;
61 esac
62 case "$require_current_main" in
63 true | false) ;;
64 *)
65 echo "::error::REQUIRE_CURRENT_MAIN must be true or false, got: $require_current_main" >&2
66 exit 2
67 ;;
68 esac
69 case "$verify_checkout" in
70 true | false) ;;
71 *)
72 echo "::error::VERIFY_RELEASE_CHECKOUT must be true or false, got: $verify_checkout" >&2
73 exit 2
74 ;;
75 esac
76
77 stable_tag_pattern='^desktop-v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-([0-9A-Za-z-]+)(\.[0-9A-Za-z-]+)*)?)?$'
78 preview_tag_pattern='^desktop-v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)-preview\.(0|[1-9][0-9]*)$'
79
80 case "$channel" in
81 stable)
82 if [[ ! "$tag" =~ $stable_tag_pattern ]] || [[ "$tag" =~ $preview_tag_pattern ]]; then
83 echo "::error::Stable Desktop candidate requires desktop-vMAJOR.MINOR.PATCH[-PRERELEASE], excluding -preview.N: $tag" >&2
84 exit 1
85 fi
86 ;;
87 preview)
88 if [[ ! "$tag" =~ $preview_tag_pattern ]]; then
89 echo "::error::Preview Desktop candidate requires desktop-vMAJOR.MINOR.PATCH-preview.N: $tag" >&2
90 exit 1
91 fi
92 ;;
93 *)
94 echo "::error::Desktop release channel must be stable or preview, got: $channel" >&2
95 exit 2
96 ;;
97 esac
98
99 git fetch "$release_remote" main-v2 --tags
100 main_sha="$(git rev-parse "$release_remote/main-v2^{commit}")"
101
102 if [ "$orchestrated" = "true" ]; then
103 candidate="$approved_sha"
104 else
105 if [ "$caller_event" != "workflow_dispatch" ] ||
106 [ "$caller_ref" != "refs/heads/main-v2" ] ||
107 [ "$caller_ref_protected" != "true" ]; then
108 echo "::error::standalone Desktop releases must run from protected main-v2" >&2
109 exit 1
110 fi
111 if [ "$caller_sha" != "$caller_workflow_sha" ]; then
112 echo "::error::standalone Desktop workflow SHA is $caller_workflow_sha, expected caller SHA $caller_sha" >&2
113 exit 1
114 fi
115 if [ "$channel" = "preview" ]; then
116 candidate="$caller_sha"
117 else
118 candidate="$(git rev-parse "$tag^{commit}")"
119 fi
120 fi
121
122 if [[ ! "$candidate" =~ ^[0-9a-f]{40}$ ]]; then
123 echo "::error::Desktop candidate SHA must be a full commit SHA, got: $candidate" >&2
124 exit 1
125 fi
126 if ! git cat-file -e "$candidate^{commit}" 2>/dev/null; then
127 echo "::error::Desktop candidate commit is unavailable: $candidate" >&2
128 exit 1
129 fi
130 if ! git merge-base --is-ancestor "$candidate" "$main_sha"; then
131 echo "::error::Desktop candidate $candidate is not on main-v2 history at $main_sha" >&2
132 exit 1
133 fi
134
135 if [ "$channel" = "stable" ]; then
136 if [ "$candidate_preparation" = true ]; then
137 if [ "$candidate_rehearsal" != true ] && git show-ref --verify --quiet "refs/tags/$tag"; then
138 echo "::error::candidate preparation refuses an existing release tag: $tag" >&2
139 exit 1
140 fi
141 else
142 tag_sha="$(git rev-parse "$tag^{commit}")"
143 if [ "$tag_sha" != "$candidate" ]; then
144 echo "::error::$tag points to $tag_sha, expected Desktop candidate $candidate" >&2
145 exit 1
146 fi
147 fi
148 elif git show-ref --verify --quiet "refs/tags/$tag"; then
149 echo "::error::Desktop Preview uses an immutable asset directory, not a Git tag: $tag" >&2
150 exit 1
151 elif [ "$orchestrated" != "true" ] && [ "$require_current_main" = "true" ] && [ "$candidate" != "$main_sha" ]; then
152 echo "::error::Desktop Preview must use current main-v2 $main_sha, got: $candidate" >&2
153 exit 1
154 fi
155
156 if [ "$verify_checkout" = "true" ]; then
157 head_sha="$(git rev-parse HEAD^{commit})"
158 if [ "$head_sha" != "$candidate" ]; then
159 echo "::error::Desktop release checkout is $head_sha, expected candidate $candidate" >&2
160 exit 1
161 fi
162 fi
163
164 echo "sha=$candidate" >>"${GITHUB_OUTPUT:-/dev/stdout}"
165 echo "Desktop candidate verified: channel=$channel tag=$tag sha=$candidate"
166
166 lines BASH