11name : PR New Environment
22
33on :
4+ workflow_dispatch :
45 pull_request_target :
56 types :
67 - opened
@@ -94,17 +95,13 @@ jobs:
9495 echo "new_envs_json=${NEW_ENVS_JSON}" >> "$GITHUB_OUTPUT"
9596
9697 deploy-and-health-check :
97- name : Deploy and Validate New Environments
98+ name : Validate New Environments
9899 needs : detect-new-envs
99100 if : needs.detect-new-envs.outputs.has_new_envs == 'true'
100101 runs-on : ubuntu-latest
101102 strategy :
102103 matrix :
103104 environment : ${{ fromJSON(needs.detect-new-envs.outputs.new_envs_json) }}
104- env :
105- HF_TOKEN : ${{ secrets.HF_PR_TOKEN }}
106- HF_NAMESPACE : ${{ vars.HF_PR_NAMESPACE }}
107- SPACE_SUFFIX : -pr-${{ github.event.number }}
108105 steps :
109106 - name : Checkout PR code
110107 uses : actions/checkout@v4
@@ -114,144 +111,136 @@ jobs:
114111 fetch-depth : 0
115112 persist-credentials : false
116113
117- - name : Default Hugging Face namespace
118- if : env.HF_NAMESPACE == ''
119- shell : bash
120- run : echo "HF_NAMESPACE=openenv-testing" >> "$GITHUB_ENV"
121-
122- - name : Verify Hugging Face token
123- shell : bash
124- run : |
125- if [ -z "${HF_TOKEN:-}" ]; then
126- echo "HF_TOKEN secret is required for deployment." >&2
127- exit 1
128- fi
129-
130- - name : Install Hugging Face CLI
131- shell : bash
132- run : |
133- curl -LsSf https://hf.co/cli/install.sh | bash
134- echo "$HOME/.local/bin" >> "$GITHUB_PATH"
114+ - name : Set up Python
115+ uses : actions/setup-python@v5
116+ with :
117+ python-version : ' 3.11'
135118
136- - name : Deploy environment to Hugging Face
119+ - name : Install OpenEnv package
137120 shell : bash
138121 run : |
139122 set -euo pipefail
140- chmod +x scripts/deploy_to_hf.sh
141- ./scripts/deploy_to_hf.sh --env "${{ matrix.environment }}" --space-suffix "${SPACE_SUFFIX}" --hub-tag "openenv-pr"
123+ python -m pip install --upgrade pip
124+ pip install .
142125
143- - name : Wait for deployment to stabilize
144- shell : bash
145- run : sleep 180
146-
147- - name : Compute Space URLs
148- id : urls
126+ - name : Run openenv validate --verbose
127+ id : validate
149128 shell : bash
150129 run : |
151- set -euo pipefail
130+ set -u -o pipefail
131+ env_dir="src/envs/${{ matrix.environment }}"
152132
153- if [ -z "${HF_NAMESPACE:-}" ]; then
154- echo "HF_NAMESPACE is not configured; unable to compute space URLs." >&2
133+ if [ ! -d "$env_dir" ]; then
134+ echo "Environment directory not found: $env_dir" >&2
135+ echo "status=failure" >> "$GITHUB_OUTPUT"
136+ printf "details<<EOF\n%s\nEOF\n" "Environment directory not found" >> "$GITHUB_OUTPUT"
155137 exit 1
156138 fi
157139
158- namespace_slug=$(echo "${HF_NAMESPACE}" | tr '[:upper:]' '[:lower:]' | tr '_' '-')
159- space_name="${{ matrix.environment }}${SPACE_SUFFIX}"
160- space_slug=$(echo "${space_name}" | tr '[:upper:]' '[:lower:]' | tr '_' '-')
161- health_url="https://${namespace_slug}-${space_slug}.hf.space/health"
162- live_url="https://${namespace_slug}-${space_slug}.hf.space"
163- space_repo_url="https://huggingface.co/spaces/${HF_NAMESPACE}/${space_name}"
164-
165- echo "namespace_slug=${namespace_slug}" >> "$GITHUB_OUTPUT"
166- echo "space_name=${space_name}" >> "$GITHUB_OUTPUT"
167- echo "space_slug=${space_slug}" >> "$GITHUB_OUTPUT"
168- echo "health_url=${health_url}" >> "$GITHUB_OUTPUT"
169- echo "live_url=${live_url}" >> "$GITHUB_OUTPUT"
170- echo "space_repo_url=${space_repo_url}" >> "$GITHUB_OUTPUT"
171-
172- - name : Perform environment health check
173- id : health_check
174- continue-on-error : true
175- shell : bash
176- env :
177- HEALTH_URL : ${{ steps.urls.outputs.health_url }}
178- SPACE_NAME : ${{ steps.urls.outputs.space_name }}
179- run : |
180- set -euo pipefail
140+ cd "$env_dir"
141+ echo "Running openenv validate --verbose in $(pwd)"
181142
182- if [ -z "${HEALTH_URL:-}" ]; then
183- echo "HEALTH_URL not provided; cannot perform health check." >&2
184- exit 1
143+ output_file="$(mktemp)"
144+ if openenv validate --verbose | tee "$output_file"; then
145+ status="success"
146+ exit_code=0
147+ else
148+ status="failure"
149+ exit_code=$?
185150 fi
186151
187- echo "Checking health for ${SPACE_NAME} at ${HEALTH_URL}"
152+ log_contents="$(cat "$output_file")"
153+ rm -f "$output_file"
188154
189- success=0
190- for attempt in {1..5}; do
191- status=$(curl -sS -o response.json -w "%{http_code}" "$HEALTH_URL" || echo "000")
192- if [ "$status" = "200" ]; then
193- echo "Health check passed for ${SPACE_NAME}"
194- cat response.json
195- success=1
196- break
197- fi
198- echo "Attempt ${attempt} returned status ${status}. Retrying in 30 seconds..."
199- sleep 30
200- done
155+ echo "status=${status}" >> "$GITHUB_OUTPUT"
156+ printf "details<<EOF\n%s\nEOF\n" "$log_contents" >> "$GITHUB_OUTPUT"
201157
202- if [ $success -ne 1 ]; then
203- echo "Health check failed for ${SPACE_NAME}" >&2
204- if [ -f response.json ]; then
205- echo "Last response payload:"
206- cat response.json
207- fi
208- exit 1
209- fi
158+ exit "$exit_code"
210159
211160 - name : Comment on PR with deployment status
212161 if : always()
213162 uses : actions/github-script@v7
214163 env :
215- HEALTH_CONCLUSION : ${{ steps.health_check.conclusion }}
216- SPACE_NAME : ${{ steps.urls.outputs.space_name }}
217- LIVE_URL : ${{ steps.urls.outputs.live_url }}
218- SPACE_REPO_URL : ${{ steps.urls.outputs.space_repo_url }}
164+ VALIDATION_STATUS : ${{ steps.validate.outputs.status }}
165+ VALIDATION_LOG : ${{ steps.validate.outputs.details }}
219166 ENV_NAME : ${{ matrix.environment }}
167+ COMMENT_TAG : " <!-- openenv-pr-preview -->"
220168 with :
221169 github-token : ${{ secrets.GITHUB_TOKEN }}
222170 script : |
223- const status = process.env.HEALTH_CONCLUSION || 'failure';
224- const spaceName = process.env.SPACE_NAME;
225- const liveUrl = process.env.LIVE_URL;
226- const repoUrl = process.env.SPACE_REPO_URL;
171+ const status = (process.env.VALIDATION_STATUS || '').toLowerCase() === 'success' ? 'success' : 'failure';
227172 const envName = process.env.ENV_NAME;
173+ const marker = process.env.COMMENT_TAG;
228174
229175 const header = status === 'success'
230- ? `✅ Deployment succeeded for \`${envName}\``
231- : `⚠️ Deployment failed for \`${envName}\``;
176+ ? `✅ Validation succeeded for \`${envName}\``
177+ : `⚠️ Validation failed for \`${envName}\``;
232178
233179 const summary = status === 'success'
234- ? 'Nice work! Wait for a code review and we\'re ready to go.'
235- : 'Please resolve your environment.';
236-
237- const body = [
180+ ? 'Your env passes the vibe check. However, most environments should go straight to the hub, they will automatically be added to the official Env Hub collection on a nightly basis. Environments in the official specification repo are only meant to demonstrate usage of a specific spec feature for educational purposes. Re-run locally with:'
181+ : 'Validation reported issues. Review the log and re-run locally with `openenv validate --verbose`. Please note, we recently changed the standard template, your environment might pre-date this standard, follow the conversion guide https://github.com/meta-pytorch/OpenEnv/blob/main/scripts/CONVERT.md to convert your environment to the new standard.';
182+
183+ const envDir = 'src/envs/' + envName;
184+ const rawLog = process.env.VALIDATION_LOG || '';
185+ const trimmedLog = rawLog.trim();
186+ const maxLength = 6000;
187+ let displayLog = trimmedLog;
188+ if (displayLog.length > maxLength) {
189+ displayLog = displayLog.slice(0, maxLength) + '\n... (truncated)';
190+ }
191+
192+ const bodyLines = [
193+ marker,
194+ '',
238195 header,
239196 '',
240- `- Space repo: [${repoUrl}](${repoUrl})`,
241- `- Live URL: [${liveUrl}](${liveUrl})`,
242197 '',
243198 summary,
244199 '',
245- 'You can iterate locally or validate fixes by running `scripts/deploy_to_hf.sh --env "' + envName + '"`.'
246- ].join('\n');
247-
248- await github.rest.issues.createComment({
249- owner: context.repo.owner,
250- repo: context.repo.repo,
251- issue_number: context.payload.pull_request.number,
252- body
253- });
254-
255- - name : Fail job if health check failed
256- if : steps.health_check.conclusion == 'failure'
257- run : exit 1
200+ '- `openenv validate --verbose ' + envDir + '`',
201+ '',
202+ '```\n' + displayLog + '\n```',
203+ 'You can deploy the environment to Hugging Face Spaces by running `openenv push`.'
204+ ];
205+
206+ const {owner, repo} = context.repo;
207+ const issue_number = context.payload.pull_request.number;
208+ const serverUrl = process.env.GITHUB_SERVER_URL || 'https://github.com';
209+ const runUrl = `${serverUrl}/${owner}/${repo}/actions/runs/${context.runId}`;
210+
211+ if (status !== 'success') {
212+ bodyLines.push(`- Failed run: ${runUrl}`);
213+ } else {
214+ bodyLines.push(`- Success run: ${runUrl}`);
215+ }
216+
217+ const bodyText = bodyLines.join('\n');
218+
219+ const existing = await github.paginate(
220+ github.rest.issues.listComments,
221+ { owner, repo, issue_number, per_page: 100 },
222+ (response, done) => {
223+ const match = response.data.find(comment => comment.body && comment.body.includes(marker));
224+ if (match) {
225+ done();
226+ return [match];
227+ }
228+ return [];
229+ }
230+ );
231+
232+ if (existing.length > 0) {
233+ await github.rest.issues.updateComment({
234+ owner,
235+ repo,
236+ comment_id: existing[0].id,
237+ body: bodyText,
238+ });
239+ } else {
240+ await github.rest.issues.createComment({
241+ owner,
242+ repo,
243+ issue_number,
244+ body: bodyText,
245+ });
246+ }
0 commit comments