Skip to content

Commit a775a1d

Browse files
SC-85409 | Build and deploy demonstration previews on pull requests (#1352)
### Goal: - To create timely PR previews when contributing or updating demos. --- ### Summary: This pull request uses the new `v2-build-demos` and `v2-deploy-demos` workflows to: 1. Build "modified" and new demos. 2. Save them as artifacts. 3. Deploy to the SWC database as previews. 4. Post a comment with preview links. --- ### Other changes: - Improved descriptions of inputs for workflows. --- ### Testing: I was required to fork the QML repo to push these workflows to `master` as `workflow_run` uses the workflow context from `master`. Therefore, you can take a look at the following ran actions: https://github.com/Alan-eMartin/qml/actions <img width="776" alt="Screenshot 2025-04-23 at 2 55 06 PM" src="https://github.com/user-attachments/assets/06273e69-ea21-4162-aed7-2181e7aabfae" /> --------- Co-authored-by: Paul Finlay <[email protected]>
1 parent 78b6669 commit a775a1d

File tree

5 files changed

+313
-13
lines changed

5 files changed

+313
-13
lines changed
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
name: V2 Build and Deploy Demos
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
environment:
7+
description: SWC environment to deploy to
8+
options:
9+
- swc-staging
10+
- swc-prod
11+
- swc-dev
12+
required: true
13+
type: choice
14+
target:
15+
default: stable
16+
description: PennyLane version to build the demos. Either 'latest' or the most recent 'stable' release.
17+
options:
18+
- latest
19+
- stable
20+
required: true
21+
type: choice
22+
demos:
23+
description: Demos to build and deploy, space-separated list of slugs (e.g. "demo1 demo2 demo3"), or leave empty for all demos.
24+
required: false
25+
type: string
26+
as-previews:
27+
default: false
28+
description: |
29+
Whether to deploy the demos as previews.
30+
31+
**Please note** that demos built with the latest version cannot be published to swc-staging or swc-prod.
32+
They can only be deployed as previews.
33+
required: false
34+
type: boolean
35+
36+
jobs:
37+
validate-and-parse-inputs:
38+
runs-on: ubuntu-latest
39+
outputs:
40+
branch: ${{ steps.set-branch.outputs.branch }}
41+
steps:
42+
- name: Set branch
43+
id: set-branch
44+
run: |
45+
if [[ "${{ github.event.inputs.target }}" == "stable" ]]; then
46+
echo "branch=master" >> $GITHUB_OUTPUT
47+
elif [[ "${{ github.event.inputs.target }}" == "latest" ]]; then
48+
echo "branch=dev" >> $GITHUB_OUTPUT
49+
else
50+
echo "branch=" >> $GITHUB_OUTPUT
51+
fi
52+
53+
- name: Validate preview input
54+
id: validate-preview
55+
run: |
56+
if [[
57+
("${{ github.event.inputs.environment }}" == "swc-staging" ||
58+
"${{ github.event.inputs.environment }}" == "swc-prod") &&
59+
"${{ github.event.inputs.target }}" == "latest" &&
60+
"${{ github.event.inputs.as-previews }}" == "false"
61+
]]; then
62+
echo "========================="
63+
echo "🚫 Invalid input detected:"
64+
echo "Demos built with the latest version cannot be published to 'swc-staging' or 'swc-prod'."
65+
echo "They can only be deployed as previews."
66+
echo "Please set the 'as-previews' input to 'true' in your workflow configuration."
67+
echo "========================="
68+
exit 1
69+
fi
70+
71+
build:
72+
needs: validate-and-parse-inputs
73+
if: >
74+
(needs.validate-and-parse-inputs.outputs.branch == 'master') ||
75+
(needs.validate-and-parse-inputs.outputs.branch == 'dev')
76+
uses: ./.github/workflows/v2-build-demos.yml
77+
with:
78+
ref: ${{ needs.validate-and-parse-inputs.outputs.branch }}
79+
demo-names: ${{ github.event.inputs.demos }}
80+
dev: ${{ github.event.inputs.target == 'latest' }}
81+
save-artifact: true
82+
artifact-name: build-and-deploy-${{ github.event.inputs.target }}
83+
keep-going: false
84+
quiet: false
85+
batch_size: 10
86+
87+
deploy:
88+
uses: ./.github/workflows/v2-deploy-demos.yml
89+
needs:
90+
- validate-and-parse-inputs
91+
- build
92+
secrets: inherit
93+
with:
94+
environment: ${{ github.event.inputs.environment }}
95+
artifact-name: build-and-deploy-${{ github.event.inputs.target }}
96+
preview: ${{ github.event.inputs.as-previews }}
97+
branch: ${{ needs.validate-and-parse-inputs.outputs.branch }}

.github/workflows/v2-build-demos.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ on:
88
type: string
99
demo-names:
1010
description: |
11-
Only build the demos specified in this list.
11+
Only build the demos specified in a space-separated list.
12+
e.g. demo1 demo2 demo3
13+
14+
If not specified, all demos will be built.
1215
required: false
1316
type: string
1417
default: ''
@@ -212,5 +215,3 @@ jobs:
212215
uses: geekyeggo/delete-artifact@v5
213216
with:
214217
name: ${{ needs.generate-build-variables.outputs.artifact-name }}-c*
215-
216-

.github/workflows/v2-build-pr.yml

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
name: V2 Build PR
2+
3+
on:
4+
pull_request:
5+
# remove v2 after testing
6+
branches: [master, dev, v2]
7+
8+
permissions:
9+
contents: read
10+
11+
concurrency:
12+
group: build-v2-demos-${{ github.event.pull_request.head.sha }}
13+
cancel-in-progress: true
14+
15+
jobs:
16+
# Step 1: Identify changed demos
17+
identify-changed-demos:
18+
runs-on: ubuntu-latest
19+
outputs:
20+
updated: ${{ steps.get-changed-demos.outputs.updated }}
21+
deleted: ${{ steps.get-changed-demos.outputs.deleted }}
22+
23+
steps:
24+
- name: Checkout
25+
uses: actions/checkout@v4
26+
27+
- name: Get changed demos
28+
id: get-changed-demos
29+
uses: ./.github/actions/get-changed-demos
30+
31+
- name: Output changed demos
32+
run: |
33+
echo "Updated Demos: ${{ steps.get-changed-demos.outputs.updated }}"
34+
echo "Deleted Demos: ${{ steps.get-changed-demos.outputs.deleted }}"
35+
36+
- name: Exit if no changes
37+
if: steps.get-changed-demos.outputs.updated == ''
38+
run: |
39+
echo "No changes found in demos. Exiting workflow."
40+
41+
# Step 2: Build demos
42+
build:
43+
if: needs.identify-changed-demos.outputs.updated != ''
44+
uses: ./.github/workflows/v2-build-demos.yml
45+
needs:
46+
- identify-changed-demos
47+
with:
48+
ref: ${{ github.event.pull_request.head.sha }}
49+
demo-names: ${{ needs.identify-changed-demos.outputs.updated }}
50+
dev: ${{ github.event.pull_request.base.ref == 'dev' }}
51+
save-artifact: true
52+
artifact-name: demo-build-${{ github.event.pull_request.head.sha }}
53+
keep-going: false
54+
quiet: false
55+
batch_size: 10
56+
57+
# Step 3: Save build context
58+
save-build-context:
59+
runs-on: ubuntu-latest
60+
needs:
61+
- build
62+
- identify-changed-demos
63+
steps:
64+
- name: Save Pull Request Event Context
65+
run: |
66+
mkdir -p /tmp/pr
67+
cat >/tmp/pr/pr_info.json <<EOL
68+
{
69+
"id": "${{ github.event.pull_request.number }}",
70+
"ref": "${{ github.event.pull_request.head.sha }}",
71+
"ref_name": "${{ github.event.pull_request.head.ref }}",
72+
"updated_demos": "${{ needs.identify-changed-demos.outputs.updated }}",
73+
"deleted_demos": "${{ needs.identify-changed-demos.outputs.deleted }}"
74+
}
75+
EOL
76+
77+
- name: Upload Pull Request Event Context as Artifact
78+
uses: actions/upload-artifact@v4
79+
with:
80+
name: pr_info
81+
path: /tmp/pr
82+
retention-days: 30

.github/workflows/v2-deploy-demos.yml

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,6 @@ on:
1313
description: Whether to deploy demos as preview
1414
type: boolean
1515
required: true
16-
branch:
17-
description: Branch to checkout
18-
type: string
19-
default: sc-81925-qml-repository-ci-pushes-newly-built-demos
2016

2117
workflow_dispatch:
2218
inputs:
@@ -31,10 +27,6 @@ on:
3127
description: Whether to deploy demos as preview
3228
type: boolean
3329
required: true
34-
branch:
35-
description: Branch to checkout
36-
type: string
37-
default: sc-81925-qml-repository-ci-pushes-newly-built-demos
3830

3931
jobs:
4032
deploy-demos:
@@ -46,7 +38,6 @@ jobs:
4638
- name: Checkout
4739
uses: actions/checkout@v4
4840
with:
49-
ref: ${{ inputs.branch }}
5041
fetch-depth: 1
5142

5243
- name: Configure AWS credentials
@@ -75,4 +66,3 @@ jobs:
7566
python3 .github/workflows/qml_pipeline_v2/deploy.py \
7667
--preview ${{ inputs.preview }} \
7768
_build/pack/*.zip
78-

.github/workflows/v2-deploy-pr.yml

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
name: V2 Deploy PR
2+
3+
on:
4+
workflow_run:
5+
workflows:
6+
- "V2 Build PR"
7+
types:
8+
- completed
9+
10+
permissions:
11+
actions: read
12+
pull-requests: write
13+
contents: read
14+
15+
concurrency:
16+
group: deploy-v2-demos-${{ github.event.workflow_run.head_branch }}
17+
cancel-in-progress: true
18+
19+
jobs:
20+
# Step 1: Prepare the build context
21+
prepare-build-context:
22+
if: github.event.workflow_run.conclusion == 'success'
23+
runs-on: ubuntu-latest
24+
steps:
25+
- name: Download Build Context
26+
uses: XanaduAI/cloud-actions/download-github-workflow-artifact@main
27+
with:
28+
workflow_run_id: ${{ github.event.workflow_run.id }}
29+
artifact_name_regex: '^pr_info$'
30+
github_token: ${{ github.token }}
31+
32+
- name: Check if Build Context file exists
33+
id: build_context
34+
env:
35+
context_artifact_file_name: pr_info.zip
36+
run: |
37+
if test -f "$context_artifact_file_name"; then
38+
echo "result=$context_artifact_file_name" >> $GITHUB_OUTPUT
39+
fi
40+
41+
- name: Unpack Build Information
42+
if: steps.build_context.outputs.result != ''
43+
run: unzip ${{ steps.build_context.outputs.result }}
44+
45+
- name: Read Build Information
46+
id: read_build_info
47+
if: steps.build_context.outputs.result != ''
48+
uses: actions/github-script@v7
49+
with:
50+
script: |
51+
const fs = require('fs');
52+
const buildData = fs.readFileSync('pr_info.json', 'utf8');
53+
return JSON.parse(buildData);
54+
55+
- name: Parse Pull Request Event Information
56+
id: pr_info
57+
if: github.event.workflow_run.event == 'pull_request' && steps.build_context.outputs.result != ''
58+
run: |
59+
echo '${{ steps.read_build_info.outputs.result }}' | jq -r '.id' > pr_id.txt
60+
echo '${{ steps.read_build_info.outputs.result }}' | jq -r '.ref' > pr_ref.txt
61+
echo '${{ steps.read_build_info.outputs.result }}' | jq -r '.ref_name' > pr_ref_name.txt
62+
echo '${{ steps.read_build_info.outputs.result }}' | jq -c '.updated_demos' > updated_demos.json
63+
echo '${{ steps.read_build_info.outputs.result }}' | jq -c '.deleted_demos' > deleted_demos.json
64+
65+
echo "pr_id=$(cat pr_id.txt)" >> $GITHUB_OUTPUT
66+
echo "pr_ref=$(cat pr_ref.txt)" >> $GITHUB_OUTPUT
67+
echo "pr_ref_name=$(cat pr_ref_name.txt)" >> $GITHUB_OUTPUT
68+
echo "updated_demos=$(cat updated_demos.json)" >> $GITHUB_OUTPUT
69+
echo "deleted_demos=$(cat deleted_demos.json)" >> $GITHUB_OUTPUT
70+
71+
- name: Set job outputs
72+
if: github.event.workflow_run.event == 'pull_request' && steps.build_context.outputs.result != ''
73+
id: set_job_outputs
74+
run: |
75+
echo "pr_id=${{ steps.pr_info.outputs.pr_id }}" >> $GITHUB_OUTPUT
76+
echo "pr_ref=${{ steps.pr_info.outputs.pr_ref }}" >> $GITHUB_OUTPUT
77+
echo "pr_ref_name=${{ steps.pr_info.outputs.pr_ref_name }}" >> $GITHUB_OUTPUT
78+
echo "updated_demos=${{ steps.pr_info.outputs.updated_demos }}" >> $GITHUB_OUTPUT
79+
echo "deleted_demos=${{ steps.pr_info.outputs.deleted_demos }}" >> $GITHUB_OUTPUT
80+
outputs:
81+
pr_id: ${{ steps.set_job_outputs.outputs.pr_id }}
82+
pr_ref: ${{ steps.set_job_outputs.outputs.pr_ref }}
83+
pr_ref_name: ${{ steps.set_job_outputs.outputs.pr_ref_name }}
84+
updated_demos: ${{ steps.set_job_outputs.outputs.updated_demos }}
85+
deleted_demos: ${{ steps.set_job_outputs.outputs.deleted_demos }}
86+
87+
# Step 2: Deploy the demos to SWC
88+
deploy-preview-demos:
89+
if: github.event.workflow_run.event == 'pull_request' && needs.prepare-build-context.result == 'success'
90+
uses: ./.github/workflows/v2-deploy-demos.yml
91+
needs: prepare-build-context
92+
with:
93+
# TODO: Update SWC environment to "swc-prod"
94+
environment: 'swc-staging'
95+
artifact-name: demo-build-${{ needs.prepare-build-context.outputs.pr_ref }}
96+
preview: true
97+
secrets: inherit
98+
99+
# Step 3: Create a comment on the PR with the demo links
100+
generate-comment:
101+
if: github.event.workflow_run.event == 'pull_request' && needs.prepare-build-context.outputs.pr_id != ''
102+
runs-on: ubuntu-latest
103+
needs: prepare-build-context
104+
steps:
105+
- name: Create markdown comment from demo names
106+
id: generate-markdown
107+
run: |
108+
demos="${{ needs.prepare-build-context.outputs.updated_demos }}"
109+
110+
comment="### Preview(s) are ready! :tada:\n"
111+
comment+="<details>\n"
112+
comment+="<summary>Toggle to view preview links</summary>\n"
113+
comment+="\n"
114+
# TODO: Switch to prod once testing is complete
115+
for demo in $demos; do
116+
comment+="- [$demo](https://staging.pennylane.ai/qml/demos/$demo?preview=true)\n"
117+
done
118+
119+
comment+="\n"
120+
comment+="</details>"
121+
122+
echo "markdown=$comment" >> $GITHUB_OUTPUT
123+
124+
- name: Comment on PR
125+
id: comment-on-pr
126+
uses: XanaduAI/cloud-actions/create-and-update-pull-request-comment@main
127+
with:
128+
github_token: ${{ secrets.github_token }}
129+
pull_request_number: ${{ needs.prepare-build-context.outputs.pr_id }}
130+
comment_body: ${{ steps.generate-markdown.outputs.markdown }}

0 commit comments

Comments
 (0)