Skip to content

Commit 0f8b9be

Browse files
authored
Feature/GitHub direct download (#315)
* feat: Add local registry cache for project code lookups * Implemented fallback registry support * feat: Attempt to download jdeploy-files.zip direct from github for github releases. * Generate jdeploy-files.zip and store in release directly. * Updated action to use concurrency guards on package-info.json * mock network tests for github action * added workflow for integtation tests * Update Client4JLauncher binaries from release 0.20.4 * added debugging for action etag * more debugging in action * do not upload package-info-2.json to the release * Updated for fixed registry/{code} end-point * don't download jdeploy-files from jdeploy tag * Upload package.json to github releases * Update Client4JLauncher binaries from release 0.20.5 * Update Client4JLauncher binaries from release 0.20.6 * fixed issue with generating jdeploy-files.zip * Added debug logging and fallback download locations for package-info.json for github sources * Changed download jdeploy-files end-point to new end-point * Update Client4JLauncher binaries from release 0.20.7 --------- Co-authored-by: shannah <[email protected]>
1 parent 62f3c4c commit 0f8b9be

39 files changed

+3946
-66
lines changed
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
#!/bin/bash
2+
# .github/scripts/download-jdeploy-baseline.sh
3+
# Downloads package-info.json from jdeploy tag and captures ETag for optimistic locking
4+
5+
set -e
6+
7+
# Mockable functions for testing
8+
github_api_get() {
9+
local url="$1"
10+
shift
11+
curl -s -w "\n%{http_code}" "$@" "$url"
12+
}
13+
14+
github_api_download() {
15+
local url="$1"
16+
shift
17+
curl -sS -D - "$@" "$url"
18+
}
19+
20+
write_to_github_env() {
21+
local var_name="$1"
22+
local var_value="$2"
23+
echo "${var_name}=${var_value}" >> ${GITHUB_ENV:-/dev/null}
24+
}
25+
26+
# Main script logic
27+
main() {
28+
REPOSITORY="$1"
29+
GITHUB_TOKEN="$2"
30+
31+
if [ -z "$REPOSITORY" ] || [ -z "$GITHUB_TOKEN" ]; then
32+
echo "Usage: $0 <repository> <github_token>"
33+
exit 1
34+
fi
35+
36+
REPO_URL="https://api.github.com/repos/${REPOSITORY}"
37+
38+
# Step 1: Check if jdeploy release exists
39+
echo "Checking if jdeploy release exists..."
40+
RELEASE_RESPONSE=$(github_api_get \
41+
"${REPO_URL}/releases/tags/jdeploy" \
42+
-H "Accept: application/vnd.github+json" \
43+
-H "Authorization: Bearer ${GITHUB_TOKEN}" \
44+
-H "X-GitHub-Api-Version: 2022-11-28")
45+
46+
HTTP_CODE=$(echo "$RELEASE_RESPONSE" | tail -n1)
47+
RELEASE_BODY=$(echo "$RELEASE_RESPONSE" | sed '$d')
48+
49+
if [ "$HTTP_CODE" = "404" ]; then
50+
# Case A: jdeploy tag doesn't exist - first publish
51+
write_to_github_env "JDEPLOY_TAG_EXISTS" "false"
52+
write_to_github_env "JDEPLOY_FIRST_PUBLISH" "true"
53+
echo "jdeploy tag not found - this is the first publish"
54+
exit 0
55+
elif [ "$HTTP_CODE" != "200" ]; then
56+
# Case D: Unexpected error
57+
echo "ERROR: Failed to check jdeploy release (HTTP $HTTP_CODE)"
58+
echo "$RELEASE_BODY"
59+
exit 1
60+
fi
61+
62+
write_to_github_env "JDEPLOY_TAG_EXISTS" "true"
63+
write_to_github_env "JDEPLOY_FIRST_PUBLISH" "false"
64+
RELEASE_ID=$(echo "$RELEASE_BODY" | jq -r '.id')
65+
write_to_github_env "JDEPLOY_RELEASE_ID" "$RELEASE_ID"
66+
67+
# Step 2: Download package-info.json asset with ETag
68+
echo "Downloading package-info.json from jdeploy release..."
69+
ASSETS=$(echo "$RELEASE_BODY" | jq -r '.assets')
70+
ASSET_ID=$(echo "$ASSETS" | jq -r '.[] | select(.name == "package-info.json") | .id')
71+
72+
if [ -z "$ASSET_ID" ] || [ "$ASSET_ID" = "null" ]; then
73+
# Case B: jdeploy release exists but package-info.json is missing
74+
echo "ERROR: jdeploy release exists but package-info.json asset is missing"
75+
echo "This indicates a corrupted or incomplete publish state."
76+
echo ""
77+
echo "To fix this:"
78+
echo " 1. Go to: https://github.com/${REPOSITORY}/releases"
79+
echo " 2. Delete the 'jdeploy' release (NOT the tag)"
80+
echo " 3. Re-run this workflow"
81+
exit 1
82+
fi
83+
84+
# Get asset ETag using HEAD request (faster and more reliable)
85+
ASSET_URL="${REPO_URL}/releases/assets/${ASSET_ID}"
86+
echo "Fetching ETag for package-info.json asset..."
87+
88+
# Use HEAD request with redirect following to get headers from final location
89+
# GitHub returns a 302 redirect to the actual asset location
90+
HEADERS_RESPONSE=$(curl -sS -L -I \
91+
-H "Accept: application/octet-stream" \
92+
-H "Authorization: Bearer ${GITHUB_TOKEN}" \
93+
-H "X-GitHub-Api-Version: 2022-11-28" \
94+
"${ASSET_URL}")
95+
96+
# Extract ETag from headers - get the last occurrence (after redirects)
97+
ETAG=$(echo "$HEADERS_RESPONSE" | grep -i '^etag:' | tail -1 | sed 's/[Ee][Tt]ag: *//i' | tr -d '\r\n' | tr -d '"' | tr -d ' ')
98+
99+
if [ -z "$ETAG" ]; then
100+
echo "WARNING: Could not capture ETag from package-info.json"
101+
echo "Optimistic locking will not be available for this publish."
102+
echo "This means concurrent publishes may overwrite each other."
103+
# Set a placeholder to allow continuation but signal no locking
104+
ETAG=""
105+
else
106+
echo "Captured ETag: $ETAG"
107+
fi
108+
109+
write_to_github_env "PACKAGE_INFO_ETAG" "$ETAG"
110+
echo "Successfully downloaded baseline package-info.json (ETag: $ETAG)"
111+
}
112+
113+
# Run main if not being sourced (for testing)
114+
if [ "${BASH_SOURCE[0]}" = "${0}" ]; then
115+
main "$@"
116+
fi
Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
#!/usr/bin/env bats
2+
3+
# Test suite for download-jdeploy-baseline.sh using bats
4+
5+
setup() {
6+
# Source the script to get access to functions
7+
# But prevent it from running by stubbing main logic
8+
export GITHUB_ENV=$(mktemp)
9+
export SCRIPT_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")" && pwd)"
10+
source "$SCRIPT_DIR/download-jdeploy-baseline.sh" || true
11+
}
12+
13+
teardown() {
14+
rm -f "$GITHUB_ENV"
15+
}
16+
17+
# Mock functions for testing
18+
mock_github_api_get_404() {
19+
echo ""
20+
echo "404"
21+
}
22+
23+
mock_github_api_get_200_with_asset() {
24+
cat <<'EOF'
25+
{"id": 12345, "assets": [{"id": 67890, "name": "package-info.json"}]}
26+
200
27+
EOF
28+
}
29+
30+
mock_github_api_get_200_without_asset() {
31+
cat <<'EOF'
32+
{"id": 12345, "assets": []}
33+
200
34+
EOF
35+
}
36+
37+
mock_github_api_get_500() {
38+
echo '{"message": "Internal Server Error"}'
39+
echo "500"
40+
}
41+
42+
mock_github_api_download_with_etag() {
43+
cat <<'EOF'
44+
HTTP/2 200
45+
etag: "abc123def456"
46+
content-type: application/json
47+
48+
{"version": "1.0.0"}
49+
EOF
50+
}
51+
52+
mock_github_api_download_no_etag() {
53+
cat <<'EOF'
54+
HTTP/2 200
55+
content-type: application/json
56+
57+
{"version": "1.0.0"}
58+
EOF
59+
}
60+
61+
# Test 1: Case A - First publish (404 on jdeploy tag)
62+
@test "download-baseline: handles first publish (404)" {
63+
# Override the github_api_get function
64+
github_api_get() { mock_github_api_get_404; }
65+
66+
# Run main script logic
67+
run bash -c '
68+
source .github/scripts/download-jdeploy-baseline.sh
69+
github_api_get() { echo ""; echo "404"; }
70+
71+
REPOSITORY="test/repo"
72+
GITHUB_TOKEN="fake-token"
73+
REPO_URL="https://api.github.com/repos/test/repo"
74+
75+
RELEASE_RESPONSE=$(github_api_get "${REPO_URL}/releases/tags/jdeploy")
76+
HTTP_CODE=$(echo "$RELEASE_RESPONSE" | tail -n1)
77+
RELEASE_BODY=$(echo "$RELEASE_RESPONSE" | sed '\''$d'\'')
78+
79+
if [ "$HTTP_CODE" = "404" ]; then
80+
write_to_github_env "JDEPLOY_TAG_EXISTS" "false"
81+
write_to_github_env "JDEPLOY_FIRST_PUBLISH" "true"
82+
echo "jdeploy tag not found - this is the first publish"
83+
exit 0
84+
fi
85+
'
86+
87+
[ "$status" -eq 0 ]
88+
[[ "$output" == *"jdeploy tag not found"* ]]
89+
}
90+
91+
# Test 2: Case B - Corrupted state (release exists but no asset)
92+
@test "download-baseline: detects corrupted state (no package-info.json asset)" {
93+
run bash -c '
94+
source .github/scripts/download-jdeploy-baseline.sh
95+
github_api_get() {
96+
cat <<EOF
97+
{"id": 12345, "assets": []}
98+
200
99+
EOF
100+
}
101+
102+
REPOSITORY="test/repo"
103+
GITHUB_TOKEN="fake-token"
104+
REPO_URL="https://api.github.com/repos/test/repo"
105+
106+
RELEASE_RESPONSE=$(github_api_get "${REPO_URL}/releases/tags/jdeploy")
107+
HTTP_CODE=$(echo "$RELEASE_RESPONSE" | tail -n1)
108+
RELEASE_BODY=$(echo "$RELEASE_RESPONSE" | sed '\''$d'\'')
109+
110+
if [ "$HTTP_CODE" = "200" ]; then
111+
ASSETS=$(echo "$RELEASE_BODY" | jq -r ".assets")
112+
ASSET_ID=$(echo "$ASSETS" | jq -r ".[] | select(.name == \"package-info.json\") | .id")
113+
114+
if [ -z "$ASSET_ID" ] || [ "$ASSET_ID" = "null" ]; then
115+
echo "ERROR: jdeploy release exists but package-info.json asset is missing"
116+
exit 1
117+
fi
118+
fi
119+
'
120+
121+
[ "$status" -eq 1 ]
122+
[[ "$output" == *"package-info.json asset is missing"* ]]
123+
}
124+
125+
# Test 3: Case C - Normal update (release and asset exist)
126+
@test "download-baseline: handles normal update with ETag capture" {
127+
run bash -c '
128+
source .github/scripts/download-jdeploy-baseline.sh
129+
github_api_get() {
130+
cat <<EOF
131+
{"id": 12345, "assets": [{"id": 67890, "name": "package-info.json"}]}
132+
200
133+
EOF
134+
}
135+
136+
github_api_download() {
137+
cat <<EOF
138+
HTTP/2 200
139+
etag: "abc123def456"
140+
content-type: application/json
141+
142+
{"version": "1.0.0"}
143+
EOF
144+
}
145+
146+
REPOSITORY="test/repo"
147+
GITHUB_TOKEN="fake-token"
148+
REPO_URL="https://api.github.com/repos/test/repo"
149+
150+
# Step 1: Get release
151+
RELEASE_RESPONSE=$(github_api_get "${REPO_URL}/releases/tags/jdeploy")
152+
HTTP_CODE=$(echo "$RELEASE_RESPONSE" | tail -n1)
153+
RELEASE_BODY=$(echo "$RELEASE_RESPONSE" | sed '\''$d'\'')
154+
155+
if [ "$HTTP_CODE" = "200" ]; then
156+
RELEASE_ID=$(echo "$RELEASE_BODY" | jq -r ".id")
157+
write_to_github_env "JDEPLOY_RELEASE_ID" "$RELEASE_ID"
158+
159+
# Step 2: Get asset
160+
ASSETS=$(echo "$RELEASE_BODY" | jq -r ".assets")
161+
ASSET_ID=$(echo "$ASSETS" | jq -r ".[] | select(.name == \"package-info.json\") | .id")
162+
163+
if [ -n "$ASSET_ID" ] && [ "$ASSET_ID" != "null" ]; then
164+
# Download and extract ETag
165+
DOWNLOAD_RESPONSE=$(github_api_download "${REPO_URL}/releases/assets/${ASSET_ID}")
166+
ETAG=$(echo "$DOWNLOAD_RESPONSE" | grep -i "^etag:" | sed "s/etag: *//i" | tr -d "\r\n" | tr -d "\"")
167+
168+
write_to_github_env "PACKAGE_INFO_ETAG" "$ETAG"
169+
echo "Successfully downloaded baseline package-info.json (ETag: $ETAG)"
170+
fi
171+
fi
172+
'
173+
174+
[ "$status" -eq 0 ]
175+
[[ "$output" == *"Successfully downloaded baseline"* ]]
176+
[[ "$output" == *"abc123def456"* ]]
177+
}
178+
179+
# Test 4: Case D - Server error (500)
180+
@test "download-baseline: handles server error (500)" {
181+
run bash -c '
182+
source .github/scripts/download-jdeploy-baseline.sh
183+
github_api_get() {
184+
echo "{\"message\": \"Internal Server Error\"}"
185+
echo "500"
186+
}
187+
188+
REPOSITORY="test/repo"
189+
GITHUB_TOKEN="fake-token"
190+
REPO_URL="https://api.github.com/repos/test/repo"
191+
192+
RELEASE_RESPONSE=$(github_api_get "${REPO_URL}/releases/tags/jdeploy")
193+
HTTP_CODE=$(echo "$RELEASE_RESPONSE" | tail -n1)
194+
RELEASE_BODY=$(echo "$RELEASE_RESPONSE" | sed '\''$d'\'')
195+
196+
if [ "$HTTP_CODE" != "200" ] && [ "$HTTP_CODE" != "404" ]; then
197+
echo "ERROR: Failed to check jdeploy release (HTTP $HTTP_CODE)"
198+
exit 1
199+
fi
200+
'
201+
202+
[ "$status" -eq 1 ]
203+
[[ "$output" == *"ERROR: Failed to check jdeploy release"* ]]
204+
[[ "$output" == *"500"* ]]
205+
}
206+
207+
# Test 5: Missing ETag in response
208+
@test "download-baseline: fails when ETag is missing" {
209+
run bash -c '
210+
source .github/scripts/download-jdeploy-baseline.sh
211+
github_api_download() {
212+
cat <<EOF
213+
HTTP/2 200
214+
content-type: application/json
215+
216+
{"version": "1.0.0"}
217+
EOF
218+
}
219+
220+
DOWNLOAD_RESPONSE=$(github_api_download "fake-url")
221+
ETAG=$(echo "$DOWNLOAD_RESPONSE" | grep -i "^etag:" | sed "s/etag: *//i" | tr -d "\r\n" | tr -d "\"")
222+
223+
if [ -z "$ETAG" ]; then
224+
echo "ERROR: Failed to capture ETag from package-info.json download"
225+
exit 1
226+
fi
227+
'
228+
229+
[ "$status" -eq 1 ]
230+
[[ "$output" == *"Failed to capture ETag"* ]]
231+
}
232+
233+
# Test 6: write_to_github_env function works correctly
234+
@test "write_to_github_env: writes variables correctly" {
235+
run bash -c '
236+
source .github/scripts/download-jdeploy-baseline.sh
237+
export GITHUB_ENV=$(mktemp)
238+
239+
write_to_github_env "TEST_VAR" "test_value"
240+
write_to_github_env "ANOTHER_VAR" "another_value"
241+
242+
cat "$GITHUB_ENV"
243+
rm -f "$GITHUB_ENV"
244+
'
245+
246+
[ "$status" -eq 0 ]
247+
[[ "$output" == *"TEST_VAR=test_value"* ]]
248+
[[ "$output" == *"ANOTHER_VAR=another_value"* ]]
249+
}

0 commit comments

Comments
 (0)