microsoft/qdk

Public

mirrored fromhttps://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
fedimser/qmem

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

.ado/publish.yml

586lines · modecode

1name: qdk-publish-$(BuildId)
2
3# Run on merges to main to ensure that the latest code
4# is always able to be published.
5trigger:
6 branches:
7 include:
8 - main
9
10# Run the pipeline every day at 6:00 AM to ensure
11# codeql and other governance checks are up-to-date.
12schedules:
13 - cron: "0 6 * * *"
14 displayName: "Build for Component Governance"
15 branches:
16 include:
17 - main
18 always: true
19
20variables:
21 CARGO_TERM_COLOR: always
22 RUST_TOOLCHAIN_VERSION: "1.93"
23
24resources:
25 repositories:
26 - repository: 1ESPipelineTemplates
27 type: git
28 name: 1ESPipelineTemplates/1ESPipelineTemplates
29 ref: refs/tags/release
30
31parameters:
32 - name: matrix
33 type: object
34 default:
35 - name: linux_x86_64
36 poolName: "Azure-Pipelines-DevTools-GPU"
37 imageName: "1es-pt-dsvm-ubuntu-2204"
38 os: linux
39 arch: x86_64
40 envVars:
41 QDK_GPU_TESTS: "1"
42 - name: linux_aarch64
43 poolName: "Azure-Pipelines-DevTools-ARM64-EO"
44 imageName: "azurelinux-3.0-gen2-arm64"
45 os: linux
46 arch: aarch64
47 - name: mac_x86_64
48 poolName: "Azure Pipelines"
49 imageName: "macOS-latest" # MacOS-specific Py (Mac is usually quite limited).
50 os: macOS
51 arch: x86_64
52 - name: mac_aarch64
53 poolName: "AcesShared"
54 os: macOS
55 arch: aarch64
56 envVars:
57 QDK_GPU_TESTS: "1"
58 - name: windows_x86_64
59 poolName: "Azure-Pipelines-DevTools-GPU"
60 imageName: "1es-pt-dsvm-windows-2022" # Win-specific Py + Platform-independent Py.
61 os: windows
62 arch: x86_64
63 envVars:
64 QDK_GPU_TESTS: "1"
65 - name: windows_aarch64
66 poolName: "Azure-Pipelines-DevTools-ARM64-EO"
67 imageName: "windows-2025-gen2-arm64"
68 os: windows
69 arch: aarch64
70
71# variables set by pipeline
72# - BASE_IMAGE
73# - BUILD_NUMBER
74# - BUILD_TYPE
75# - cratesIoFeedOverride
76# - OwnerPersonalAlias
77# - PAT
78# - toolchainFeed
79
80extends:
81 template: v1/1ES.Official.PipelineTemplate.yml@1ESPipelineTemplates
82 parameters:
83 sdl:
84 sourceAnalysisPool:
85 name: "Azure-Pipelines-DevTools-EO"
86 image: windows-2025
87 os: windows
88 stages:
89 - stage: build
90 displayName: Build
91 jobs:
92 - job: "Node"
93 pool:
94 name: "Azure-Pipelines-DevTools-EO"
95 image: "ubuntu-latest"
96 os: linux
97 timeoutInMinutes: 90
98 templateContext:
99 outputs:
100 - output: pipelineArtifact
101 displayName: "Upload NPM Package Artifact"
102 targetPath: $(System.DefaultWorkingDirectory)/target/npm/qsharp
103 artifactName: NPM
104 condition: succeeded()
105 steps:
106 # common init steps
107 - task: RustInstaller@1
108 inputs:
109 rustVersion: ms-prod-$(RUST_TOOLCHAIN_VERSION)
110 additionalTargets: wasm32-unknown-unknown
111 cratesIoFeedOverride: $(cratesIoFeedOverride)
112 toolchainFeed: $(toolchainFeed)
113 displayName: Install Rust toolchain
114
115 - task: UsePythonVersion@0
116 inputs:
117 versionSpec: "3.11"
118 githubToken: "$(GH_PACKAGE_READ_TOKEN)"
119
120 - task: UseNode@1
121 inputs:
122 version: "22.x"
123
124 - script: |
125 python ./prereqs.py --install && python ./version.py
126 displayName: Install Prereqs and set version
127
128 # build steps
129
130 - script: |
131 python build.py --npm --wasm --no-check-prereqs
132 displayName: Build VSCode Extension
133
134 - script: |
135 mkdir -p $(System.DefaultWorkingDirectory)/target/npm/qsharp
136 npm pack --pack-destination $(System.DefaultWorkingDirectory)/target/npm/qsharp
137 displayName: Pack NPM Package
138 workingDirectory: $(System.DefaultWorkingDirectory)/source/npm/qsharp
139
140 - job: "VSCode"
141 pool:
142 name: "Azure-Pipelines-DevTools-EO"
143 image: "ubuntu-latest"
144 os: linux
145 timeoutInMinutes: 90
146 templateContext:
147 outputs:
148 - output: pipelineArtifact
149 displayName: "Upload VSCode Extension Artifact"
150 targetPath: $(System.DefaultWorkingDirectory)/target/vscode
151 artifactName: VSIX
152 condition: succeeded()
153 steps:
154 # common init steps
155 - task: RustInstaller@1
156 inputs:
157 rustVersion: ms-prod-$(RUST_TOOLCHAIN_VERSION)
158 additionalTargets: wasm32-unknown-unknown
159 cratesIoFeedOverride: $(cratesIoFeedOverride)
160 toolchainFeed: $(toolchainFeed)
161 displayName: Install Rust toolchain
162
163 - task: UsePythonVersion@0
164 inputs:
165 versionSpec: "3.11"
166 githubToken: "$(GH_PACKAGE_READ_TOKEN)"
167
168 - task: UseNode@1
169 inputs:
170 version: "22.x"
171
172 - script: |
173 python ./prereqs.py --install && python ./version.py
174 displayName: Install Prereqs and set version
175
176 # Below VS Code extension build only needs to run on one platform (Linux x86_64 for now)
177 - script: |
178 npm install -g @vscode/vsce
179 displayName: Install Prereqs for VSCode Extension
180
181 # build steps
182
183 - script: |
184 python build.py --wasm --npm --vscode --integration-tests --no-check-prereqs
185 displayName: Build VSCode Extension
186
187 - script: |
188 vsce package --pre-release
189 mkdir -p $(System.DefaultWorkingDirectory)/target/vscode
190 mv *.vsix $(System.DefaultWorkingDirectory)/target/vscode
191 condition: and(succeeded(), eq(variables['BUILD_TYPE'], 'dev'))
192 displayName: Pack pre-release VSCode Extension
193 workingDirectory: "$(System.DefaultWorkingDirectory)/source/vscode"
194
195 - script: |
196 vsce package
197 mkdir -p $(System.DefaultWorkingDirectory)/target/vscode
198 mv *.vsix $(System.DefaultWorkingDirectory)/target/vscode
199 condition: and(succeeded(), ne(variables['BUILD_TYPE'], 'dev'))
200 displayName: Pack VSCode Extension
201 workingDirectory: "$(System.DefaultWorkingDirectory)/source/vscode"
202
203 - script: |
204 VSIX_RPATH="$(System.DefaultWorkingDirectory)/target/vscode"
205 VSIX_FNAME=`ls $VSIX_RPATH/*.vsix`
206 echo "RPATH: $VSIX_RPATH"
207 echo "FNAME: $VSIX_FNAME"
208
209 VSIX_FNAME_NOEXT="${VSIX_FNAME%.vsix}"
210 MANIFEST_FILE="${VSIX_FNAME_NOEXT}.manifest"
211 SIGNATURE_FILE="${VSIX_FNAME_NOEXT}.signature.p7s"
212
213 vsce generate-manifest -i $VSIX_FNAME -o $MANIFEST_FILE
214 cp $MANIFEST_FILE $SIGNATURE_FILE
215
216 echo "##vso[task.setvariable variable=VSIX_FNAME;]$VSIX_FNAME"
217 echo "##vso[task.setvariable variable=MANIFEST_FILE;]$MANIFEST_FILE"
218 echo "##vso[task.setvariable variable=SIGNATURE_FILE;]$SIGNATURE_FILE"
219 displayName: Prepare VSCode Extension Manifest and Signature for Signing
220
221 - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@6
222 displayName: "ESRP CodeSigning"
223 condition: succeeded()
224 inputs:
225 ConnectedServiceName: "PME ESRP Azure Connection"
226 AppRegistrationClientId: "ced85fbc-381c-467d-958e-ee86a128e63a"
227 AppRegistrationTenantId: "975f013f-7f24-47e8-a7d3-abc4752bf346"
228 EsrpClientId: "832c049d-cd07-4c1c-bfa5-c07250d190cb"
229 UseMSIAuthentication: true
230 AuthAKVName: "quantum-esrp-kv"
231 AuthSignCertName: ESRPCert
232 FolderPath: "target/vscode"
233 Pattern: "*.signature.p7s"
234 signConfigType: inlineSignParams
235 inlineOperation: |
236 [
237 {
238 "keyCode": "CP-401405",
239 "operationSetCode": "VSCodePublisherSign",
240 "parameters" : [],
241 "toolName": "sign",
242 "toolVersion": "1.0"
243 }
244 ]
245 SessionTimeout: 90
246 MaxConcurrency: 25
247 MaxRetryAttempts: 5
248 PendingAnalysisWaitTimeoutMinutes: 5
249
250 - script: |
251 echo "Command: vsce verify-signature --packagePath $VSIX_FNAME --manifestPath $MANIFEST_FILE --signaturePath $SIGNATURE_FILE"
252 vsce verify-signature --packagePath $VSIX_FNAME --manifestPath $MANIFEST_FILE --signaturePath $SIGNATURE_FILE
253 condition: succeeded()
254 displayName: Confirming signature verification before publishing
255
256 - job: "Platform_Agnostic_Python"
257 pool:
258 name: "Azure-Pipelines-DevTools-EO"
259 image: "ubuntu-latest"
260 os: linux
261 timeoutInMinutes: 90
262 templateContext:
263 outputs:
264 - output: pipelineArtifact
265 displayName: "Upload Platform-Agnostic Python Artifacts"
266 targetPath: $(System.DefaultWorkingDirectory)/target/wheels/
267 artifactName: Wheels.PlatformAgnostic
268 condition: succeeded()
269 steps:
270 # common init steps
271 - task: RustInstaller@1
272 inputs:
273 rustVersion: ms-prod-$(RUST_TOOLCHAIN_VERSION)
274 cratesIoFeedOverride: $(cratesIoFeedOverride)
275 toolchainFeed: $(toolchainFeed)
276 displayName: Install Rust toolchain
277
278 - task: UsePythonVersion@0
279 inputs:
280 versionSpec: "3.11"
281 githubToken: "$(GH_PACKAGE_READ_TOKEN)"
282
283 - task: UseNode@1
284 inputs:
285 version: "22.x"
286
287 - script: |
288 python ./prereqs.py --skip-wasm && python ./version.py
289 displayName: Install Prereqs and set version
290
291 # The jupyterlab and widgets packages have no tests. The qdk package has minimal tests but
292 # they also depend on the qsharp package which is platform-dependent. So we skip tests here
293 # and rely on the GitHub CI to run the few `qdk` tests on every PR.
294 - script: |
295 python ./build.py --jupyterlab --widgets --qdk --no-check --no-test --no-check-prereqs
296 displayName: Build Platform-Agnostic Packages
297
298 - script: |
299 ls target/wheels/*
300
301 - ${{ each target in parameters.matrix }}:
302 - job: Python_${{ target.name }}_job
303 pool:
304 name: ${{ target.poolName }}
305 image: ${{ target.imageName }}
306 os: ${{ target.os }}
307 ${{ if and(eq(target.arch, 'aarch64'), eq(target.os, 'linux')) }}:
308 hostArchitecture: Arm64
309 ${{ if eq(target.poolName, 'AcesShared') }}:
310 demands:
311 - ImageOverride -equals ACES_VM_SharedPool_Sequoia
312 variables:
313 arch: ${{ target.arch }}
314 additionalRustTargets: ${{ target.additionalRustTargets }}
315 ${{ if target.envVars }}:
316 ${{ each envVar in target.envVars }}:
317 ${{ envVar.key }}: ${{ envVar.value }}
318 timeoutInMinutes: 90
319 templateContext:
320 outputs:
321 - output: pipelineArtifact
322 displayName: "Upload Python Artifacts Mac"
323 condition: and(succeeded(), eq(variables['Agent.OS'], 'Darwin'))
324 targetPath: $(System.DefaultWorkingDirectory)/target/wheels
325 artifactName: Wheels.Mac.${{ target.arch }}
326 - output: pipelineArtifact
327 displayName: "Upload Python Artifacts Win"
328 condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
329 targetPath: $(System.DefaultWorkingDirectory)/target/wheels
330 artifactName: Wheels.Win.${{ target.arch }}
331 - output: pipelineArtifact
332 displayName: "Upload Python Artifacts Linux"
333 condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))
334 targetPath: $(System.DefaultWorkingDirectory)/target/wheels
335 artifactName: Wheels.Linux.${{ target.arch }}
336 steps:
337 # common init steps
338 # if we have additional rust targets, we need to install them
339 - task: RustInstaller@1
340 inputs:
341 rustVersion: ms-prod-$(RUST_TOOLCHAIN_VERSION)
342 additionalTargets: wasm32-unknown-unknown
343 cratesIoFeedOverride: $(cratesIoFeedOverride)
344 toolchainFeed: $(toolchainFeed)
345 displayName: Install Rust toolchain
346
347 - script: |
348 rustc --version
349 rustc --print target-list
350 displayName: View rust target info
351
352 - task: UsePythonVersion@0
353 inputs:
354 versionSpec: "3.11"
355 githubToken: "$(GH_PACKAGE_READ_TOKEN)"
356 ${{ if eq(target.arch, 'aarch64') }}:
357 architecture: "arm64"
358 condition: not(and(eq(variables['Agent.OS'], 'Linux'), eq(variables['arch'], 'aarch64')))
359
360 # UsePythonVersion@0 does not support aarch64, so we install Python manually
361 # using tdnf on Azure Linux 3.0 aarch64
362 # We also need alias python3 as python
363 - script: |
364 sudo tdnf install python3-3.12.9 python3-pip-24.2 -y
365 sudo alternatives --install /usr/bin/python python /usr/bin/python3 1
366 displayName: Install Python on Azure Linux 3.0 aarch64
367 condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'), eq(variables['arch'], 'aarch64'))
368
369 - task: UseNode@1
370 inputs:
371 version: "22.x"
372
373 - script: |
374 sudo apt update
375 sudo apt install -y patchelf
376 displayName: Install Linux x86_64 prereqs
377 condition: and(eq(variables['Agent.OS'], 'Linux'), eq(variables['arch'], 'x86_64'))
378
379 # on Azure Linux 3.0 aarch64 we need to install:
380 # ld with binutils
381 # crt1.o and others with glibc-devel
382 # std lib headers with kernel-headers
383 # patchelf for modifying ELF files via auditwheel
384 - script: |
385 sudo tdnf install binutils glibc-devel kernel-headers patchelf gcc -y
386 displayName: Install c++ build tools on Azure Linux 3.0 aarch64
387 condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'), eq(variables['arch'], 'aarch64'))
388
389 - script: |
390 python ./prereqs.py --skip-wasm
391 displayName: Install Prereqs
392 condition: and(ne(variables['Agent.OS'], 'Linux'), ne(variables['arch'], 'aarch64'))
393
394 - script: |
395 python ./version.py
396 displayName: Set version
397
398 # Windows arm64
399 - script: |
400 python build.py --pip --no-check-prereqs --no-integration-tests --no-optional-dependencies
401 displayName: Build Platform-Dependent Py Packages
402 condition: and(eq(variables['Agent.OS'], 'Windows_NT'), eq(variables['arch'], 'aarch64'))
403
404 # every other platform
405 - script: |
406 python build.py --pip --no-check-prereqs --integration-tests
407 displayName: Build Platform-Dependent Py Packages
408 condition: not(and(eq(variables['Agent.OS'], 'Windows_NT'), eq(variables['arch'], 'aarch64')))
409
410 - script: |
411 dir target\wheels\*
412 displayName: List Py Packages on Win
413 condition: eq(variables['Agent.OS'], 'Windows_NT')
414
415 - script: |
416 ls target/wheels/*
417 displayName: List Py Packages on non-Win
418 condition: ne(variables['Agent.OS'], 'Windows_NT')
419
420 - stage: approval
421 displayName: Approval
422 dependsOn: build
423 condition: and(succeeded(), eq(variables['Build.Reason'], 'Manual'))
424 jobs:
425 - job: "Approval"
426 pool: server
427 timeoutInMinutes: 1440 # job times out in 1 day
428 steps:
429 - task: ManualValidation@0
430 timeoutInMinutes: 1440 # task times out in 1 day
431 inputs:
432 notifyUsers: ""
433 instructions: "Please verify artifacts and approve the release"
434 onTimeout: "reject"
435
436 - stage: release
437 displayName: Release
438 dependsOn: approval
439 condition: and(succeeded(), eq(variables['Build.Reason'], 'Manual'))
440 jobs:
441 - job: "Publish_VSIX_Package"
442 pool:
443 name: "Azure-Pipelines-DevTools-EO"
444 image: "ubuntu-latest"
445 os: linux
446 templateContext:
447 type: releaseJob
448 isProduction: true
449 inputs: # All input build artifacts must be declared here
450 - input: pipelineArtifact
451 artifactName: VSIX
452 targetPath: $(System.DefaultWorkingDirectory)/target/vscode
453 steps:
454 - script: |
455 npm install -g @vscode/vsce
456 displayName: Install Prereqs for VSCode Ext Publishing
457
458 - script: |
459 VSIX_RPATH="$(System.DefaultWorkingDirectory)/target/vscode"
460 VSIX_FNAME=`ls $VSIX_RPATH/*.vsix`
461
462 VSIX_FNAME_NOEXT="${VSIX_FNAME%.vsix}"
463 MANIFEST_FILE="${VSIX_FNAME_NOEXT}.manifest"
464 SIGNATURE_FILE="${VSIX_FNAME_NOEXT}.signature.p7s"
465
466 echo "##vso[task.setvariable variable=VSIX_FNAME;]$VSIX_FNAME"
467 echo "##vso[task.setvariable variable=MANIFEST_FILE;]$MANIFEST_FILE"
468 echo "##vso[task.setvariable variable=SIGNATURE_FILE;]$SIGNATURE_FILE"
469 displayName: Get VSCode Extension, Manifest, and Signature files for Publishing
470
471 - script: |
472 echo "Command: vsce verify-signature --packagePath $VSIX_FNAME --manifestPath $MANIFEST_FILE --signaturePath $SIGNATURE_FILE"
473 vsce verify-signature --packagePath $VSIX_FNAME --manifestPath $MANIFEST_FILE --signaturePath $SIGNATURE_FILE
474 condition: succeeded()
475 displayName: Reconfirming signature verification before publishing
476
477 - script: |
478 echo "Command: vsce publish --pre-release --packagePath $VSIX_FNAME --manifestPath $MANIFEST_FILE --signaturePath $SIGNATURE_FILE"
479 vsce publish --pre-release --packagePath $VSIX_FNAME --manifestPath $MANIFEST_FILE --signaturePath $SIGNATURE_FILE 2>&1 > pub.log
480 condition: and(succeeded(), eq(variables['BUILD_TYPE'], 'dev'))
481 displayName: Publish pre-release VSCode Extension
482 env:
483 VSCE_PAT: $(PAT)
484
485 - script: |
486 echo "Command: vsce publish --packagePath $VSIX_FNAME --manifestPath $MANIFEST_FILE --signaturePath $SIGNATURE_FILE"
487 vsce publish --packagePath $VSIX_FNAME --manifestPath $MANIFEST_FILE --signaturePath $SIGNATURE_FILE 2>&1 > pub.log
488 condition: and(succeeded(), ne(variables['BUILD_TYPE'], 'dev'))
489 displayName: Publish VSCode Extension
490 env:
491 VSCE_PAT: $(PAT)
492
493 - script: |
494 dir
495 cat pub.log
496 displayName: If failed, display VSCode Publishing Results
497 condition: failed()
498 workingDirectory: "$(System.DefaultWorkingDirectory)"
499
500 - job: "Publish_Python_Packages"
501 pool:
502 name: "Azure-Pipelines-DevTools-EO"
503 image: "ubuntu-latest"
504 os: linux
505 templateContext:
506 type: releaseJob
507 isProduction: true
508 inputs: # All input build artifacts must be declared here
509 - input: pipelineArtifact
510 artifactName: Wheels.Win.x86_64
511 targetPath: $(System.DefaultWorkingDirectory)/artifacts/win-x86_64
512 - input: pipelineArtifact
513 artifactName: Wheels.Win.aarch64
514 targetPath: $(System.DefaultWorkingDirectory)/artifacts/win-aarch64
515 - input: pipelineArtifact
516 artifactName: Wheels.Mac.x86_64
517 targetPath: $(System.DefaultWorkingDirectory)/artifacts/mac-x86_64
518 - input: pipelineArtifact
519 artifactName: Wheels.Mac.aarch64
520 targetPath: $(System.DefaultWorkingDirectory)/artifacts/mac-aarch64
521 - input: pipelineArtifact
522 artifactName: Wheels.Linux.x86_64
523 targetPath: $(System.DefaultWorkingDirectory)/artifacts/linux-x86_64
524 - input: pipelineArtifact
525 artifactName: Wheels.Linux.aarch64
526 targetPath: $(System.DefaultWorkingDirectory)/artifacts/linux-aarch64
527 - input: pipelineArtifact
528 artifactName: Wheels.PlatformAgnostic
529 targetPath: $(System.DefaultWorkingDirectory)/artifacts/platform-agnostic
530 steps:
531 - script: |
532 mkdir -p $(System.DefaultWorkingDirectory)/target/wheels
533 find $(System.DefaultWorkingDirectory)/artifacts -name "*.whl" -exec cp {} $(System.DefaultWorkingDirectory)/target/wheels/ \;
534 ls $(System.DefaultWorkingDirectory)/target/wheels
535 displayName: Collect and Display Py Artifacts in Publishing Dir
536
537 - task: EsrpRelease@9
538 condition: succeeded()
539 displayName: Publish Py Packages
540 inputs:
541 connectedservicename: "PME ESRP Azure Connection"
542 usemanagedidentity: true
543 keyvaultname: "quantum-esrp-kv"
544 signcertname: ESRPCert
545 clientid: "832c049d-cd07-4c1c-bfa5-c07250d190cb"
546 contenttype: PyPi
547 domaintenantid: "975f013f-7f24-47e8-a7d3-abc4752bf346"
548 folderlocation: "$(System.DefaultWorkingDirectory)/target/wheels"
549 waitforreleasecompletion: true
550 owners: "billti@microsoft.com"
551 approvers: "billti@microsoft.com"
552 mainpublisher: ESRPRELPACMAN
553
554 - job: "Publish_NPM_Package"
555 pool:
556 name: "Azure-Pipelines-DevTools-EO"
557 image: "ubuntu-latest"
558 os: linux
559 templateContext:
560 type: releaseJob
561 isProduction: true
562 inputs: # All input build artifacts must be declared here
563 - input: pipelineArtifact
564 artifactName: NPM
565 targetPath: $(System.DefaultWorkingDirectory)/target/npm/qsharp
566 steps:
567 - script: |
568 ls $(System.DefaultWorkingDirectory)/target/npm/qsharp/*
569 displayName: Display NPM Artifacts in Publishing Dir
570
571 - task: EsrpRelease@9
572 condition: succeeded()
573 displayName: Publish NPM Package
574 inputs:
575 connectedservicename: "PME ESRP Azure Connection"
576 usemanagedidentity: true
577 keyvaultname: "quantum-esrp-kv"
578 signcertname: ESRPCert
579 clientid: "832c049d-cd07-4c1c-bfa5-c07250d190cb"
580 contenttype: "NPM"
581 domaintenantid: "975f013f-7f24-47e8-a7d3-abc4752bf346"
582 folderlocation: "$(System.DefaultWorkingDirectory)/target/npm/qsharp"
583 waitforreleasecompletion: true
584 owners: "billti@microsoft.com"
585 approvers: "billti@microsoft.com"
586 mainpublisher: ESRPRELPACMAN
587