Skip to content

Commit 3307267

Browse files
authored
Merge pull request #143 from EUCPilots/microsoft-apps
Microsoft apps
2 parents 8ab2511 + 1b9604c commit 3307267

4 files changed

Lines changed: 268 additions & 0 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[
2+
{
3+
"appName": "MicrosoftScout",
4+
"downloadUrl": "https://www.microsoft.com/en-us/download/details.aspx?id=108685"
5+
}
6+
]
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
name: Update Microsoft Download Apps
2+
3+
on:
4+
schedule:
5+
# Run weekly on Wednesdays at 9:00 AM UTC
6+
- cron: '0 9 * * 3'
7+
workflow_dispatch:
8+
9+
permissions:
10+
contents: write
11+
pull-requests: write
12+
13+
jobs:
14+
update-microsoft-downloads:
15+
runs-on: ubuntu-latest
16+
17+
steps:
18+
- name: Checkout repository
19+
uses: actions/checkout@v4
20+
with:
21+
fetch-depth: 0
22+
token: ${{ secrets.PAT_TOKEN || secrets.GITHUB_TOKEN }}
23+
24+
- name: Setup Python
25+
uses: actions/setup-python@v5
26+
with:
27+
python-version: '3.11'
28+
29+
- name: Install dependencies
30+
run: |
31+
pip install requests beautifulsoup4 lxml
32+
33+
- name: Parse Microsoft download pages and update manifests
34+
id: update-manifests
35+
run: |
36+
python3 << 'EOF'
37+
import requests
38+
import json
39+
import re
40+
import sys
41+
import os
42+
43+
config_path = ".github/microsoft-download-apps.json"
44+
45+
with open(config_path, 'r') as f:
46+
apps = json.load(f)
47+
48+
changes_made = False
49+
updated_apps = []
50+
51+
for app in apps:
52+
app_name = app['appName']
53+
download_url = app['downloadUrl']
54+
manifest_path = f"Manifests/{app_name}.json"
55+
56+
print(f"\nProcessing {app_name}")
57+
print(f"URL: {download_url}")
58+
59+
try:
60+
response = requests.get(download_url, timeout=30, headers={
61+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
62+
})
63+
response.raise_for_status()
64+
65+
# Extract the __DLCDetails__ JSON from the script tag
66+
match = re.search(
67+
r'window\.__DLCDetails__\s*=\s*(\{.*?\})\s*</script>',
68+
response.text,
69+
re.DOTALL
70+
)
71+
72+
if not match:
73+
print(f"Error: Could not find __DLCDetails__ JSON on page for {app_name}")
74+
sys.exit(1)
75+
76+
dlc_data = json.loads(match.group(1))
77+
all_files = dlc_data['dlcDetailsView']['downloadFile']
78+
79+
# Filter to Windows installer file types only
80+
windows_extensions = ('.exe', '.msi', '.msix', '.msixbundle', '.zip')
81+
windows_files = [
82+
f for f in all_files
83+
if f['url'].lower().split('?')[0].endswith(windows_extensions)
84+
]
85+
86+
if not windows_files:
87+
print(f"Error: No Windows installer files found for {app_name}")
88+
sys.exit(1)
89+
90+
version = windows_files[0]['version']
91+
date_published = windows_files[0]['datePublished']
92+
uris = [f['url'] for f in windows_files]
93+
94+
print(f"Found version: {version}")
95+
print(f"Found date: {date_published}")
96+
print(f"Found {len(uris)} Windows installer(s)")
97+
for uri in uris:
98+
print(f" {uri}")
99+
100+
# Validate version format (1-4 part semantic version)
101+
if not re.match(r'^\d+(\.\d+){1,3}$', version):
102+
print(f"Error: Unexpected version format: {version}")
103+
sys.exit(1)
104+
105+
# Read the manifest
106+
with open(manifest_path, 'r') as f:
107+
manifest = json.load(f)
108+
109+
old_version = manifest['Get']['Download'].get('Version', '')
110+
old_uris = manifest['Get']['Download'].get('Uri', '')
111+
old_date = manifest['Get']['Download'].get('Date', '')
112+
113+
new_uris = uris if len(uris) > 1 else uris[0]
114+
115+
# Update version, URIs, and date
116+
manifest['Get']['Download']['Version'] = version
117+
manifest['Get']['Download']['Uri'] = new_uris
118+
# Only set Date if the manifest already has that field
119+
if 'Date' in manifest['Get']['Download']:
120+
manifest['Get']['Download']['Date'] = date_published
121+
122+
# Write updated manifest
123+
with open(manifest_path, 'w') as f:
124+
json.dump(manifest, f, indent=4)
125+
# Ensure trailing newline
126+
with open(manifest_path, 'a') as f:
127+
f.write('\n')
128+
129+
if old_version != version or old_uris != new_uris or old_date != date_published:
130+
print(f"Changes detected for {app_name}:")
131+
if old_version != version:
132+
print(f" Version: {old_version} -> {version}")
133+
if old_uris != new_uris:
134+
print(f" URI(s) updated")
135+
if old_date != date_published:
136+
print(f" Date: {old_date} -> {date_published}")
137+
changes_made = True
138+
updated_apps.append(f"{app_name} -> {version} ({download_url})")
139+
else:
140+
print(f"No changes for {app_name} (already at {version})")
141+
142+
except FileNotFoundError:
143+
print(f"Error: Manifest not found at {manifest_path}")
144+
sys.exit(1)
145+
except Exception as e:
146+
print(f"Error processing {app_name}: {e}")
147+
import traceback
148+
traceback.print_exc()
149+
sys.exit(1)
150+
151+
# Write outputs
152+
github_output = os.environ.get('GITHUB_OUTPUT')
153+
if github_output:
154+
with open(github_output, 'a') as f:
155+
f.write(f"changes_made={'true' if changes_made else 'false'}\n")
156+
if updated_apps:
157+
# Join with newline literal for the PR body
158+
summary = '\n'.join(f'- {a}' for a in updated_apps)
159+
f.write(f"updated_apps<<EOF\n{summary}\nEOF\n")
160+
161+
EOF
162+
env:
163+
GITHUB_OUTPUT: ${{ github.output }}
164+
165+
- name: Check for changes
166+
id: check-changes
167+
run: |
168+
if git diff --quiet -- Manifests/; then
169+
echo "changes_detected=false" >> $GITHUB_OUTPUT
170+
echo "No changes detected"
171+
else
172+
echo "changes_detected=true" >> $GITHUB_OUTPUT
173+
echo "Changes detected"
174+
git diff -- Manifests/
175+
fi
176+
177+
- name: Commit and push changes
178+
if: steps.check-changes.outputs.changes_detected == 'true'
179+
run: |
180+
git config --global user.name 'github-actions[bot]'
181+
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
182+
git checkout -b update-microsoft-downloads
183+
git add Manifests/
184+
git commit -m 'Update Microsoft download app manifests'
185+
git push -f origin update-microsoft-downloads
186+
187+
- name: Create Pull Request
188+
if: steps.check-changes.outputs.changes_detected == 'true'
189+
env:
190+
GH_TOKEN: ${{ secrets.PAT_TOKEN || secrets.GITHUB_TOKEN }}
191+
run: |
192+
UPDATED_APPS="${{ steps.update-manifests.outputs.updated_apps }}"
193+
gh pr create \
194+
--title "Update Microsoft download app manifests" \
195+
--body "## Automated Update: Microsoft Download Pages
196+
197+
This PR updates the version and download URIs for Evergreen apps sourced from Microsoft download pages.
198+
199+
**Updated apps:**
200+
${UPDATED_APPS}
201+
202+
**Source:** .github/microsoft-download-apps.json
203+
204+
**Auto-generated by:** GitHub Actions workflow" \
205+
--base main \
206+
--head update-microsoft-downloads \
207+
--label automated || true

Apps/Get-MicrosoftScout.ps1

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
function Get-MicrosoftScout {
2+
<#
3+
.SYNOPSIS
4+
5+
#>
6+
[OutputType([System.Management.Automation.PSObject])]
7+
[CmdletBinding(SupportsShouldProcess = $false)]
8+
param (
9+
[Parameter(Mandatory = $false, Position = 0)]
10+
[ValidateNotNull()]
11+
[System.Management.Automation.PSObject]
12+
$res = (Get-FunctionResource -AppName ("$($MyInvocation.MyCommand)".Split("-"))[1])
13+
)
14+
15+
foreach ($Url in $res.Get.Download.Uri) {
16+
[PSCustomObject]@{
17+
Version = $res.Get.Download.Version
18+
Date = ConvertTo-DateTime -DateTime $res.Get.Download.Date -Pattern $res.Get.Download.DatePattern
19+
Architecture = Get-Architecture -String $Url
20+
Type = Get-FileType -File $Url
21+
Filename = (Split-Path -Path $Url -Leaf).Replace('%20', ' ')
22+
URI = $Url -replace ' ', '%20'
23+
}
24+
}
25+
}

Manifests/MicrosoftScout.json

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"Name": "Microsoft Scout",
3+
"Source": "https://learn.microsoft.com/en-au/microsoft-scout/",
4+
"Get": {
5+
"Update": {
6+
"Uri": ""
7+
},
8+
"Download": {
9+
"Version": "0.22.333",
10+
"Uri": [
11+
"https://download.microsoft.com/download/f5e1aa08-9d73-4e0b-adbd-14b2947e9b44/Scout 0.22.333/MicrosoftScout-Windows-0.22.333-arm64-Setup.exe",
12+
"https://download.microsoft.com/download/f5e1aa08-9d73-4e0b-adbd-14b2947e9b44/Scout 0.22.333/MicrosoftScout-Windows-0.22.333-x64-Setup.exe"
13+
],
14+
"Date": "6/2/2026 4:03:37 PM",
15+
"DatePattern": "M/d/yyyy h:mm:ss tt"
16+
}
17+
},
18+
"Install": {
19+
"Setup": "",
20+
"Preinstall": "",
21+
"Physical": {
22+
"Arguments": "",
23+
"PostInstall": []
24+
},
25+
"Virtual": {
26+
"Arguments": "",
27+
"PostInstall": []
28+
}
29+
}
30+
}

0 commit comments

Comments
 (0)