-
Notifications
You must be signed in to change notification settings - Fork 1
209 lines (193 loc) · 8.58 KB
/
Copy pathconnect-integration.yml
File metadata and controls
209 lines (193 loc) · 8.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
name: Connect Integration Tests
# Runs the full VIP Connect test category against ephemeral Connect servers
# stood up by `with-connect`, so we detect drift between VIP and Connect.
#
# Unlike connect-smoke.yml (a curated, fast subset gated on PRs), this workflow
# runs as many Connect tests as the default `with-connect` setup supports:
# `vip verify --api-auth --categories connect` selects every API-key scenario
# and auto-skips features the bare server doesn't have (email, data sources,
# additional runtimes). See posit-dev/connect#40047.
#
# Deliberately NOT a required check, and deliberately no `pull_request` trigger
# (decided in #421). The matrix includes `preview`, the Connect nightly build:
# gating on it would hand merge-blocking power over every VIP PR to an
# unreleased upstream build we do not control. The Connect path is already gated
# on PRs by connect-smoke.yml, whose `Connect Smoke Tests Status` job is a
# required check. This workflow is a drift detector — it runs post-merge and
# nightly, and reports failures to Slack rather than blocking anyone.
on:
push:
branches: [main]
schedule:
- cron: "0 5 * * *" # daily at 5am UTC
workflow_dispatch:
permissions:
contents: read
concurrency:
group: connect-integration-${{ github.ref }}
cancel-in-progress: true
jobs:
connect-integration:
name: Integration tests against Connect ${{ matrix.connect-version }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
# "release" = currently released stable, "preview" = nightly build.
connect-version: ["release", "preview"]
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
# Start Connect in Docker
- name: Start Connect
id: connect
uses: posit-dev/with-connect@4f26a6a301716789d6451cc3afd878afdd52189b # main
with:
version: ${{ matrix.connect-version }}
license: ${{ secrets.CONNECT_LICENSE }}
# Surface license errors as workflow annotations so expired keys
# are immediately visible on the Actions run page.
- name: Check for license errors
if: failure() && steps.connect.outcome == 'failure'
run: |
# Find the Connect container even if with-connect failed partway
CID=$(docker ps -aq --filter "ancestor=rstudio/rstudio-connect" | head -1)
if [ -z "${CID}" ]; then
echo "::error title=Connect Startup Failed::Connect container not found. The with-connect action failed before creating a container."
exit 1
fi
LOGS=$(docker logs "${CID}" 2>&1 || true)
LICENSE_MSG=$(echo "${LOGS}" | grep -i "license" | grep -iE "expired|invalid|unable" | head -1 || true)
if [ -n "${LICENSE_MSG}" ]; then
echo "::error title=License Error::${LICENSE_MSG}"
{
echo "## License Error"
echo ""
echo "The Connect license key is expired or invalid. Update the \`CONNECT_LICENSE\` repository secret."
echo ""
echo '```'
echo "${LICENSE_MSG}"
echo '```'
} >> "$GITHUB_STEP_SUMMARY"
else
echo "::error title=Connect Startup Failed::Connect failed to start. Check the 'Start Connect' step logs for details."
fi
exit 1
# Resolve the actual Connect version for unambiguous job records
- name: Resolve Connect version
id: version
env:
VIP_CONNECT_API_KEY: ${{ steps.connect.outputs.CONNECT_API_KEY }}
run: |
RESPONSE=$(curl -sf \
-H "Authorization: Key ${VIP_CONNECT_API_KEY}" \
"${{ steps.connect.outputs.CONNECT_SERVER }}/__api__/server_settings")
VERSION=$(echo "${RESPONSE}" | jq -r '.version')
if [ -z "${VERSION}" ] || [ "${VERSION}" = "null" ]; then
echo "ERROR: Could not resolve Connect version from server_settings"
echo "Response body: ${RESPONSE}"
exit 1
fi
echo "Resolved Connect version: ${VERSION}"
echo "resolved=${VERSION}" >> "$GITHUB_OUTPUT"
# Set up Python and install VIP
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
with:
enable-cache: true
- name: Install dependencies
run: uv sync
- name: Install Playwright browsers
run: uv run vip install --skip-system
# Run the full Connect category. --api-auth selects every API-key
# scenario (the bootstrap admin key from with-connect) and skips the
# browser-login scenario; features the bare server lacks (email, data
# sources, extra runtimes) auto-skip via @if_applicable.
- name: Run Connect integration tests
run: |
uv run vip verify \
--connect-url "${{ steps.connect.outputs.CONNECT_SERVER }}" \
--api-auth \
--categories connect \
--filter "not test_list_users" \
--report results.json \
-- --junitxml=connect-results.xml
env:
VIP_CONNECT_API_KEY: ${{ steps.connect.outputs.CONNECT_API_KEY }}
# Upload test results for debugging failures
- name: Upload test results
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: connect-integration-results-${{ matrix.connect-version }}
path: |
connect-results.xml
results.json
if-no-files-found: ignore
# Write a rich job summary with test details and emoji
- name: Write workflow summary
if: always()
run: |
RESOLVED="${{ steps.version.outputs.resolved }}"
CONNECT_URL="${{ steps.connect.outputs.CONNECT_SERVER }}"
REQUESTED="${{ matrix.connect-version }}"
RUN_DATE=$(date -u '+%Y-%m-%d %H:%M UTC')
{
echo "## 🔗 Connect Integration Tests — v${RESOLVED}"
echo ""
echo "| | |"
echo "|---|---|"
echo "| 🏷️ **Requested version** | \`${REQUESTED}\` |"
echo "| ✅ **Resolved version** | \`${RESOLVED}\` |"
echo "| 🌐 **Connect URL** | \`${CONNECT_URL}\` |"
echo "| 📅 **Run date** | ${RUN_DATE} |"
echo ""
} >> "$GITHUB_STEP_SUMMARY"
if [ -f connect-results.xml ]; then
TESTS=$(grep -oP '(?<=tests=")[^"]+' connect-results.xml | head -1 || echo 0)
FAILURES=$(grep -oP '(?<=failures=")[^"]+' connect-results.xml | head -1 || echo 0)
ERRORS=$(grep -oP '(?<=errors=")[^"]+' connect-results.xml | head -1 || echo 0)
SKIPPED=$(grep -oP '(?<=skipped=")[^"]+' connect-results.xml | head -1 || echo 0)
TESTS=${TESTS:-0}; FAILURES=${FAILURES:-0}; ERRORS=${ERRORS:-0}; SKIPPED=${SKIPPED:-0}
PASSED=$(( TESTS - FAILURES - ERRORS - SKIPPED ))
{
echo "### 📊 Test Results"
echo ""
echo "| Result | Count |"
echo "|--------|-------|"
echo "| ✅ Passed | ${PASSED} |"
echo "| ❌ Failed | ${FAILURES:-0} |"
echo "| ⚠️ Errors | ${ERRORS:-0} |"
echo "| ⏭️ Skipped | ${SKIPPED:-0} |"
echo "| 📝 **Total** | **${TESTS:-0}** |"
echo ""
if [ "${FAILURES:-0}" = "0" ] && [ "${ERRORS:-0}" = "0" ]; then
echo "🎉 **All tests passed!**"
else
echo "💥 **Some tests failed — check the logs for details.**"
fi
} >> "$GITHUB_STEP_SUMMARY"
else
echo "> ⚠️ Test results file not found — tests may have been skipped." >> "$GITHUB_STEP_SUMMARY"
fi
# Stop Connect
- name: Stop Connect
if: always()
uses: posit-dev/with-connect@4f26a6a301716789d6451cc3afd878afdd52189b # main
with:
stop: ${{ steps.connect.outputs.CONTAINER_ID }}
notify-on-failure:
name: Notify Slack on failure
runs-on: ubuntu-latest
needs: connect-integration
if: ${{ failure() }}
steps:
- name: Notify Slack
uses: slackapi/slack-github-action@dcb1066f776dd043e64d0e8ba94ca15cc7e1875d # v4.0.0
with:
webhook: ${{ secrets.SLACK_WEBHOOK_CONNECT_CI }}
webhook-type: incoming-webhook
payload: |
{
"text": "❌ Connect VIP tests failed — <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View Logs>"
}