Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 53 additions & 5 deletions ci/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ parameters:
displayName: "Auto-update users to this release"
type: boolean
default: false
- name: PublishStore
displayName: "Also publish to the Store"
type: boolean
default: false
- name: PreTest
displayName: "Pre test"
type: boolean
Expand All @@ -35,6 +39,10 @@ parameters:
displayName: "Test Signed"
type: boolean
default: false
- name: StoreAppId
displayName: "Microsoft Store App Id"
type: string
default: 9NQ7512CXL7T


variables:
Expand All @@ -46,7 +54,8 @@ variables:
PIP_VERBOSE: true
PYMSBUILD_VERBOSE: true
PYMSBUILD_TEMP_DIR: $(Build.BinariesDirectory)
DIST_DIR: $(Build.ArtifactStagingDirectory)
DIST_DIR: $(Build.ArtifactStagingDirectory)\dist
STORE_DIST_DIR: $(Build.ArtifactStagingDirectory)\store
LAYOUT_DIR: $(Build.BinariesDirectory)\layout
TEST_MSIX_DIR: $(Build.BinariesDirectory)\test_msix
${{ if ne(parameters.OverrideRef, '(tag)') }}:
Expand All @@ -69,6 +78,7 @@ stages:
- ${{ if eq(parameters.TestSign, 'true') }}:
- group: CPythonTestSign
- ${{ if eq(parameters.Publish, 'true') }}:
- group: MSFTStorePublish
- group: PythonOrgPublish


Expand Down Expand Up @@ -314,6 +324,13 @@ stages:
workingDirectory: $(Pipeline.Workspace)
displayName: 'Download PuTTY binaries'

- powershell: |
mv "${env:UPLOAD_DIR}\*-store.msix*" (mkdir -Force ${env:STORE_UPLOAD_DIR}) -Verbose
displayName: 'Move Store packages'
env:
UPLOAD_DIR: $(DIST_DIR)
STORE_UPLOAD_DIR: $(STORE_DIST_DIR)

- ${{ if ne(parameters.PublishAppinstaller, 'true') }}:
- powershell: |
"Not uploading these files:"
Expand All @@ -323,11 +340,42 @@ stages:
env:
UPLOAD_DIR: $(DIST_DIR)

- ${{ if eq(parameters.PublishStore, 'true') }}:
- task: UseMSStoreCLI@0
displayName: Setup Microsoft Store Developer CLI

- powershell: >
msstore reconfigure
--tenantId $(MSSTORE_TENANT_ID)
--sellerId $(MSSTORE_SELLER_ID)
--clientId $(MSSTORE_CLIENT_ID)
--clientSecret $(MSSTORE_CLIENT_SECRET)
displayName: Authenticate Store CLI

# We begin the submission but do not complete it, so the RM has a chance
# to update metadata before going public. It also means we can do this
# whether signed or not, since a test release can simply be deleted rather
# than published. Existing drafts will be overwritten with new ones.
- powershell: |
$msix = Get-Item "${env:STORE_UPLOAD_DIR}\*.msixupload"
"Uploading $msix"
msstore publish -v -nc -id $env:MSSTORE_APP_ID $msix
"MSIX is uploaded"
"Patching submission details"
python ci\store-publish.py
"Submission details updated"
"Finish publishing at https://partner.microsoft.com/en-us/dashboard/products/${env:MSSTORE_APP_ID}/overview"
displayName: 'Begin Store submission'
env:
STORE_UPLOAD_DIR: $(STORE_DIST_DIR)
MSSTORE_TENANT_ID: $(MSSTORE_TENANT_ID)
MSSTORE_SELLER_ID: $(MSSTORE_SELLER_ID)
MSSTORE_CLIENT_ID: $(MSSTORE_CLIENT_ID)
MSSTORE_CLIENT_SECRET: $(MSSTORE_CLIENT_SECRET)
MSSTORE_APP_ID: ${{ parameters.StoreAppId }}
PATCH_JSON: ci\store-patch.json

- powershell: |
# We don't want the Store MSIX on python.org, so just delete it
# It's already been archived in the earlier publish step, and is bundled
# into the .msixupload file.
del "${env:UPLOAD_DIR}\*-store.msix" -ErrorAction SilentlyContinue
python ci\upload.py
displayName: 'Publish packages'
env:
Expand Down
11 changes: 11 additions & 0 deletions ci/store-patch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"#": "This file is used by store-upload.py to modify Store metadata.",
"listings": {
"en-us":{
"baseListing": {
"#": "Update the release notes; 2-3 lines shown to Store users before updating",
"releaseNotes": "This update has a new \"first launch\" experience to help configure your system. If you don't see it, run \"py install --configure\" to start it manually.\n\nVisit https://github.com/python/pymanager for information and to provide feedback during beta releases."
}
}
}
}
91 changes: 91 additions & 0 deletions ci/store-publish.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import json
import os
import sys

from urllib.request import urlopen, Request


DIRECTORY_ID = os.environ["MSSTORE_TENANT_ID"]
CLIENT_ID = os.environ["MSSTORE_CLIENT_ID"]
CLIENT_SECRET = os.environ["MSSTORE_CLIENT_SECRET"]
SELLER_ID = os.environ["MSSTORE_SELLER_ID"]
APP_ID = os.environ["MSSTORE_APP_ID"]

PATCH_JSON = os.environ["PATCH_JSON"]
with open(PATCH_JSON, "rb") as f:
patch_data = json.load(f)


SERVICE_URL = "https://manage.devcenter.microsoft.com/v1.0/my/"

################################################################################
# Get auth token/header
################################################################################

OAUTH_URL = f"https://login.microsoftonline.com/{DIRECTORY_ID}/oauth2/v2.0/token"
SERVICE_SCOPE = "https://manage.devcenter.microsoft.com/.default"

reqAuth = Request(
OAUTH_URL,
method="POST",
headers={"Content-Type": "application/x-www-form-urlencoded"},
data=(f"grant_type=client_credentials&client_id={CLIENT_ID}&" +
f"client_secret={CLIENT_SECRET}&scope={SERVICE_SCOPE}").encode("utf-8"),
)

with urlopen(reqAuth) as r:
jwt = json.loads(r.read())

auth = {"Authorization": f"Bearer {jwt['access_token']}"}

################################################################################
# Get application data (for current submission)
################################################################################

reqApps = Request(f"{SERVICE_URL}applications/{APP_ID}", method="GET", headers=auth)
print("Getting application data from", reqApps.full_url)
with urlopen(reqApps) as r:
app_data = json.loads(r.read())

submission_url = app_data["pendingApplicationSubmission"]["resourceLocation"]

################################################################################
# Get current submission data
################################################################################

reqSubmission = Request(f"{SERVICE_URL}{submission_url}", method="GET", headers=auth)
print("Getting submission data from", reqSubmission.full_url)
with urlopen(reqSubmission) as r:
sub_data = json.loads(r.read())

################################################################################
# Patch submission data
################################################################################

if patch_data:
def _patch(target, key, src):
if key.startswith("#"):
return
if isinstance(src, dict):
for k, v in src.items():
_patch(target.setdefault(key, {}), k, v)
else:
target[key] = src

for k, v in patch_data.items():
_patch(sub_data, k, v)

################################################################################
# Update submission data
################################################################################

reqUpdate = Request(f"{SERVICE_URL}{submission_url}", method="PUT",
headers={**auth, "Content-Type": "application/json; charset=utf-8"},
data=json.dumps(sub_data).encode("utf-8"))
print("Updating submission data at", reqUpdate.full_url)
with urlopen(reqUpdate) as r:
new_data = r.read()

new_data.pop("fileUploadUrl", None)
print("Current submission metadata:")
print(json.dumps(new_data, indent=2))