@@ -87,6 +87,69 @@ _service_id() {
8787 | .serviceInstances.edges[].node | select(.serviceName==$n) | .serviceId][0] // empty' 2> /dev/null || true
8888}
8989
90+ # _refresh_status_json: re-fetch the cached status snapshot; keep the old one
91+ # if the fetch fails so a transient error cannot blank out working IDs.
92+ _refresh_status_json () {
93+ local fresh
94+ fresh=" $( railway_call status --json 2> /dev/null || true) "
95+ [ -n " $fresh " ] && RAILWAY_STATUS_JSON=" $fresh "
96+ }
97+
98+ # _service_id_with_retry <service-name> -> serviceId (empty when unresolved).
99+ # A service bootstrap created seconds earlier may not be visible in the status
100+ # snapshot yet (bootstrap→configure eventual-consistency race), so re-fetch the
101+ # snapshot and retry a few times before giving up. First-try hits (the normal
102+ # case) cost zero extra API calls and zero sleeps.
103+ _service_id_with_retry () {
104+ local service=" $1 " svc_id attempt
105+ local attempts=" ${RAILWAY_SERVICE_RESOLVE_ATTEMPTS:- 6} "
106+ local delay=" ${RAILWAY_SERVICE_RESOLVE_DELAY:- 5} "
107+
108+ for (( attempt = 1 ; attempt <= attempts; attempt++ )) ; do
109+ svc_id=" $( _service_id " $service " ) "
110+ if [ -n " $svc_id " ]; then
111+ printf ' %s' " $svc_id "
112+ return 0
113+ fi
114+ if [ " $attempt " -lt " $attempts " ]; then
115+ printf " Service id for '%s' not in status snapshot yet; re-fetching in %ds (attempt %d/%d)\n" \
116+ " $service " " $delay " " $attempt " " $attempts " >&2
117+ sleep " $delay "
118+ _refresh_status_json
119+ fi
120+ done
121+ # Empty output: the caller falls back to the CLI path.
122+ return 0
123+ }
124+
125+ # _cli_set_vars <service> KEY=VALUE ...
126+ # CLI-path variable set. "Service '<name>' not found" seconds after bootstrap
127+ # created it is the same eventual-consistency race, so retry that specific
128+ # failure a couple of times instead of failing the deploy on the first hit.
129+ # Other failures surface immediately (railway_call already retries transient
130+ # network errors internally).
131+ _cli_set_vars () {
132+ local service=" $1 "
133+ shift
134+ local attempts=" ${RAILWAY_CLI_SET_ATTEMPTS:- 3} "
135+ local delay=" ${RAILWAY_SERVICE_RESOLVE_DELAY:- 5} "
136+ local try output
137+
138+ for (( try = 1 ; try <= attempts; try++ )) ; do
139+ if output=" $( railway_call variable set --service " $service " --environment " $ENV_NAME " --skip-deploys " $@ " 2>&1 ) " ; then
140+ return 0
141+ fi
142+ if [ " $try " -lt " $attempts " ] && printf ' %s' " $output " | grep -qi " not found" ; then
143+ printf " railway variable set: service '%s' not visible yet, retrying in %ds (attempt %d/%d)\n" \
144+ " $service " " $delay " " $try " " $attempts " >&2
145+ sleep " $delay "
146+ continue
147+ fi
148+ [ -n " $output " ] && printf ' %s\n' " $output " >&2
149+ return 1
150+ done
151+ }
152+
90153# _vars_to_json KEY=VALUE ... -> {"KEY":"VALUE",...} (split on the first '=')
91154_vars_to_json () {
92155 local json=' {}' kv key val
@@ -111,18 +174,18 @@ upsert_service_vars() {
111174 [ " $# " -gt 0 ] || return 0
112175
113176 if [ -z " ${RAILWAY_API_TOKEN:- } " ] || [ -z " $RAILWAY_PROJECT_ID " ]; then
114- railway_call variable set --service " $service " --environment " $ENV_NAME " --skip-deploys " $@ " > /dev/null
177+ _cli_set_vars " $service " " $@ "
115178 return 0
116179 fi
117180
118181 local svc_id
119- svc_id=" $( _service_id " $service " ) "
182+ svc_id=" $( _service_id_with_retry " $service " ) "
120183 if [ -z " $svc_id " ]; then
121184 # Name didn't match the cached status JSON (e.g. unexpected casing). Don't
122185 # hard-fail the deploy where the CLI's --service would have worked; fall
123186 # back to it.
124187 printf " Could not resolve service id for '%s'; falling back to CLI variable set.\n" " $service " >&2
125- railway_call variable set --service " $service " --environment " $ENV_NAME " --skip-deploys " $@ " > /dev/null
188+ _cli_set_vars " $service " " $@ "
126189 return 0
127190 fi
128191
0 commit comments