-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathorb.yml
601 lines (549 loc) · 22.8 KB
/
orb.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
version: 2.1
# There is not really a manifest for this file, so I'm bumping this comment.
# version_for_git: 0.0.20
description: |
Apollo open-source build and release tooling orbs
orbs:
wait-for: cobli/wait-for@0.0.2
examples:
executors:
node:
parameters:
tag:
type: string
default: '12'
docker:
- image: circleci/node:<< parameters.tag >>
commands:
write_npmrc_for_npm_token:
description: Write an .npmrc that uses the $NPM_TOKEN environment variable
steps:
- run:
name: Writing .npmrc for $NPM_TOKEN authentication.
command: |
cat \<<'EOF' > ~/.npmrc
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
EOF
install_specific_npm_version:
description: Install a specific version of npm
parameters:
version:
type: string
default: '6'
steps:
- run:
# Due to a bug, npm upgrades from the version of npm that ships with
# Node.js 6 (npm v3.10.10) go poorly and generally causes other problems
# with the environment. Since yarn is already available here we can just
# use that to work-around the issue. It's possible that npm cleanup might
# prevent this from being necessary, but this installation can be switched
# to use `npm` (rather than `yarn`) once Node 6 is no longer tested below.
name: Install npm@<< parameters.version >>, but with yarn.
command: sudo yarn global add npm@<< parameters.version >>
npm_cache_restore:
steps:
- restore_cache:
keys:
# When lock file changes, use increasingly general patterns to restore cache
- npm-{{ .Environment.CIRCLECI_CACHE_VERSION }}-{{ .Branch }}-{{ checksum "package-lock.json" }}
- npm-{{ .Environment.CIRCLECI_CACHE_VERSION }}-{{ .Branch }}-
- npm-{{ .Environment.CIRCLECI_CACHE_VERSION }}-
npm_cache_save:
steps:
- save_cache:
key: npm-{{ .Environment.CIRCLECI_CACHE_VERSION }}-{{ .Branch }}-{{ checksum "package-lock.json" }}
paths:
# This should cache the npm cache instead of node_modules, which is needed because
# npm ci actually removes node_modules before installing to guarantee a clean slate.
- ~/.npm
npm_clean_install_with_caching:
description: |
Run 'npm ci' and utilize a cache of `~/.npm` based on cache-busting
techniques which obey changes in 'package-lock.json', the Git branch,
and the CIRCLECI_CACHE_VERSION environment variable.
steps:
- npm_cache_restore
- run: npm --version
- run: npm ci
- npm_cache_save
npm_install_with_caching:
description: |
Run 'npm i' and utilize a cache of `~/.npm` based on cache-busting
techniques which obey changes in 'package-lock.json', the Git branch,
and the CIRCLECI_CACHE_VERSION environment variable.
steps:
- npm_cache_restore
- run: npm --version
- run: npm i
- npm_cache_save
start_verdaccio:
description: Start the Verdaccio server
parameters:
config_path:
type: string
description: |
The path for the location to write the config to. The default is
the default path which Verdaccio looks for. Be sure to add double
quotes to this if the path contains spaces.
default: '$HOME/.config/verdaccio/config.yaml'
port:
type: integer
default: 4873
steps:
- run:
name: Verdaccio
command: |
npx verdaccio@5 \
--config "<< parameters.config_path >>" \
--listen << parameters.port >>
background: true
- wait-for/port:
port: << parameters.port >>
write_verdaccio_config:
description: Write Verdaccio config
parameters:
proxy_to_npmjs:
type: boolean
default: false
config_path:
type: string
description: |
The path for the location to write the config to. The default is
the default path which Verdaccio looks for. Be sure to add double
quotes to this if the path contains spaces.
default: '$HOME/.config/verdaccio/config.yaml'
skip_proxy_on_local_packages:
type: boolean
default: false
description: |
When proxy_to_npmjs is enabled, do not proxy files which are specified
in the project's "package.json" "dependencies" if those dependencies
are "file:" references. This is partially a Lernaism, but such file
references are local packages within this repository and we can use
them as a guide to define which packages we will not proxy.
steps:
- run:
name: Write Verdaccio config
command: |
# Exit on all errors, undeclared variables and pipefailures.
set -euo pipefail
<<# parameters.proxy_to_npmjs >>
<<# parameters.skip_proxy_on_local_packages >>
# Build the list of local packages for the config that we'll
# be injecting into the file that we write out next.
# Use in-line Node-fu to do it. We are capturing the output of
# the JavaScript into the local_pkg_config environment variable.
local_pkg_config="$(cat \<<'EOF' | node -p
Object.entries(require("./package.json").dependencies)
.filter(([pkg, value]) => value.startsWith("file:"))
.map(([name]) =>
` '${name}':\n access: $all\n publish: $all`)
.join("\n")
EOF
)"
<</ parameters.skip_proxy_on_local_packages >>
<</ parameters.proxy_to_npmjs >>
mkdir -p "$(dirname << parameters.config_path >>)"
cat \<<EOF > << parameters.config_path >>
# Store the files on disk. We'll use this directory as a build
# artifact and gain access to all of the packages which are
# published to this server.
storage: ./storage
# By not specifying any uplinks, we will avoid proxying through
# to npm. This allows publishing of this build's packages
# irregardless of whether they are already published to npm.
uplinks:
<<# parameters.proxy_to_npmjs >>
npmjs:
url: https://registry.npmjs.org/
<</ parameters.proxy_to_npmjs >>
# Normally, publishing isn't possible without an uplink.
publish:
allow_offline: false
# Allow any package to be published to this (local) server with
# no authentication.
packages:
<<# parameters.proxy_to_npmjs >>
<<# parameters.skip_proxy_on_local_packages >>
$local_pkg_config
<</ parameters.skip_proxy_on_local_packages >>
<</ parameters.proxy_to_npmjs >>
'**':
access: \$all
publish: \$all
<<# parameters.proxy_to_npmjs >>
proxy: npmjs
<</ parameters.proxy_to_npmjs >>
logs:
- {type: stdout, format: pretty, level: http}
EOF
save_verdaccio_storage:
description: |
Save the Verdaccio storage to a known location in the workspace.
parameters:
verdaccio_path:
type: string
description: The location of the Verdaccio installation store
default: '$HOME/.config/verdaccio/storage/'
backup_path:
type: string
description: The location where Verdaccio will be backed up to.
default: './.verdaccio-storage'
steps:
- run:
name: Save Verdaccio storage
command: |
cp -R "<< parameters.verdaccio_path >>" "<< parameters.backup_path >>"
- persist_to_workspace:
root: .
paths:
- << parameters.backup_path >>/**
publish_verdaccio_storage_as_artifacts:
steps:
- store_artifacts:
path: ./.verdaccio-storage/
destination: storage
restore_verdaccio_storage_from_backup:
description: Restore a previously backed up Verdaccio storage
parameters:
verdaccio_path:
type: string
description: The location of the Verdaccio installation store
default: '$HOME/.config/verdaccio/storage/'
backup_path:
type: string
description: The location of the Verdaccio backup to restore from.
default: './.verdaccio-storage'
steps:
- run:
name: Restoring Verdaccio storage
command: |
mkdir -p "$(dirname << parameters.verdaccio_path >>)"
cp -R "<< parameters.backup_path >>" "<< parameters.verdaccio_path >>"
write_publish_env_script:
steps:
- run:
name: Generate publish environment scripts
command: |
# Exit on all errors, undeclared variables and pipefailures.
set -euo pipefail
# Make the scripts directory, apply executable bits and write the
# script out to the file. Everything from the 'cat' down to the
# HEREDOC closing ('END_OF_SCRIPT') is written to the file. The
# contents of this file are then sourced (for their exported
# environment variables in later steps). Note that we don't chmod
# after writing the script since it ends up being a lone, easily
# missed line after 'END_OF_SCRIPT'.
mkdir -p ~/scripts/
touch ~/scripts/apollo-setup-publish-env
chmod +x ~/scripts/apollo-setup-publish-env
cat \<<'END_OF_SCRIPT' > ~/scripts/apollo-setup-publish-env
#
# NOTE! This is being output as a file, not evaluated immediately!
#
# Exit on all errors, undeclared variables and pipefailures.
set -euo pipefail
# This should be set automatically on tag-triggered builds.
if [ -z "$CIRCLE_TAG" ]; then
echo 'ERROR! $CIRCLE_TAG is not set.'
exit 1
fi
# Get the actual hash the tag points to.
tag_ref="$(git rev-list -n1 "$CIRCLE_TAG")"
# Fail if we couldn't get it.
if [ -z "$tag_ref" ]; then
echo "ERROR! Couldn't get \$tag_ref."
exit 1
fi
# Ensure the annotated tag points to a 'Release' commit.
RELEASE_COMMIT_MSG="$(git log --format=%s -n 1 $tag_ref)"
if ! echo "$RELEASE_COMMIT_MSG" | grep -qE '^Release$'; then
echo "ERROR! The 'publish/' tags must be on 'Release' commits."
exit 1
fi
# See if there are Lerna packages tagged (e.g. matching pkg@x.y.z)
# by checking what _tags_ point to the hash. The expectation is
# that there _must_ be Lerna packages tagged in the same ref that
# the `publish/` tag points to.
tags="$(git tag --points-at $tag_ref)"
non_publish_tags="$(echo "$tags" | grep -vE '^publish/[0-9]+')"
version_tags="$(echo "$non_publish_tags" | grep -E '^.+@.+$')"
if [ -z "$version_tags" ]; then
echo "ERROR! There must be at least some other packages tagged."
exit 1
fi
# Ensure the annotated publish tag is formatted expectedly.
# This could certainly be more defensive, but currently just ensures
# that the annotated message starts with 'Publish'.
publish_tag_msg="$(git tag -l --format='%(contents)' $CIRCLE_TAG)"
if ! echo "$publish_tag_msg" | grep -qE '^Publish'; then
echo "ERROR! The 'publish/' tags must be annotated correctly."
echo " Be certain to use the npm-scripts when publishing."
exit 1
fi
# If there's a dist-tag, we'll publish to that instead of 'latest'.
DIST_TAG="$(echo "$publish_tag_msg" |
sed -E 's/^Publish($|( \(dist-tag:([a-z0-9-]+)\))$)/\3/')"
# Default to latest if we couldn't extract anything from the
# annotated `publish/` tag.
if [ -z "$DIST_TAG" ]; then
DIST_TAG="latest"
fi
# Add bullet-points and links to the markdown links.
MARKDOWN_VERSIONS="$(echo "$version_tags" |
sed -E 's%^(.+)@(.+)$%• <https://npm.im/\1|\`\1@\2\`>\\n%')"
# These are exported for use in later scripts.
export DIST_TAG
export MARKDOWN_VERSIONS
END_OF_SCRIPT
jobs:
lerna_tarballs:
description: Generate tarballs from a Lerna publish
parameters:
node_version:
type: string
description: The version of Node.js to use.
default: '12'
executor:
name: node
tag: << parameters.node_version >>
steps:
# We attach the workspace and just run with it, with no other guide.
# Maybe not the most reusable pattern, but also the most common case.
- attach_workspace:
at: .
- write_verdaccio_config
- start_verdaccio
- run:
name: Publish to (local) Verdaccio
command: |
DEBUG=lerna npx lerna publish \
--no-git-tag-version \
--no-push \
--registry=http://localhost:4873/ \
--yes \
from-package
- save_verdaccio_storage
- publish_verdaccio_storage_as_artifacts
oclif_pack_from_verdaccio_storage:
description: Pack the oclif tarball from Verdaccio
parameters:
node_version:
type: string
description: The version of Node.js to use.
default: '12'
verdaccio_storage:
type: string
description: The path to the backed up Verdaccio storage
default: '.verdaccio-storage'
package:
type: string
description: The name of the package to install and pack
executor:
name: node
tag: << parameters.node_version >>
steps:
# We attach the workspace and just run with it, with no other guide.
# Maybe not the most reusable pattern, but also the most common case.
- attach_workspace:
at: .
- restore_verdaccio_storage_from_backup
- write_verdaccio_config:
proxy_to_npmjs: true
skip_proxy_on_local_packages: true
- start_verdaccio
- run:
name: oclif pack from Verdaccio-installed packages
command: |
mkdir tmp-pack-oclif/
cd tmp-pack-oclif/
# We're going to install the package to get it and generate
# a lockfile, but that necessitates that we install into a temp
# package.
npm init -y
# Install the requested package.
npm install --registry http://localhost:4873 << parameters.package >>
# Go into the installed package within node_modules/
# so we can operate on the version that was installed from the
# registry (along with any local dependencies that were installed
# from the registry)
cd "node_modules/<< parameters.package >>/"
# Generate a lockfile, since those are never published to npm, and
# the oclif-dev package absolutely requires its presence.
# In a more ideal world, we'd have a npm-shrinkwrap.json already
# present, but Lerna doesn't seem to publish that right now.
npm install --registry http://localhost:4873 --package-lock-only
# Build for Darwin
npx -p @oclif/dev-cli oclif-dev pack --targets=darwin-x64
# Move it to a known location where we can write it to artifacts.
mv ./dist/ ../../../oclif-dist/
- store_artifacts:
path: ./oclif-dist/
destination: oclif-pack
- persist_to_workspace:
root: .
paths:
- ./oclif-dist/
dry_run:
description: Dry-run
executor:
name: node
steps:
# We attach the workspace and just run with it, with no other guide.
# Maybe not the most reusable pattern, but also the most common case.
- attach_workspace:
at: .
- write_verdaccio_config:
proxy_to_npmjs: true
- start_verdaccio
- write_publish_env_script
- run:
name: Dry-run publish and Slack notification
command: |
# Exit on all errors, undeclared variables and pipefailures.
set -euo pipefail
# If this doesn't exist, we won't have what we need to proceed.
if [ ! -x ~/scripts/apollo-setup-publish-env ]; then
echo "~/scripts/apollo-setup-publish-env is not executable."
exit 1
fi
# Load our variables that we set earlier (e.g. DIST_TAG).
source ~/scripts/apollo-setup-publish-env
echo "Fake publishing to npm under the $DIST_TAG tag."
DEBUG=lerna npx lerna publish --registry=http://localhost:4873 \
--dist-tag="$DIST_TAG" from-git --yes |
tee ~/lerna-output.txt
# Sorry, not sorry. Perl is super standard on most images because
# of its continued usage in many build systems and is too easy to
# do multi-line text extraction with. Perl pie for the win.
to_publish="$(perl -0777 \
-nle 'print "$1" while /^Found.*to publish:\n(^ - .*?$)+^$/msg' \
~/lerna-output.txt)"
contains_prereleases="$(perl -0777 \
-nle 'print "yes" if /^ - \S+ => \S+\d-\S+$/m' \
~/lerna-output.txt)"
if [ -n "$contains_prereleases" ]; then
echo 'This release contains prereleases.'
if [ "$DIST_TAG" = latest ]; then
echo 'This orb will not publish a prerelease to the "latest" tag.'
exit 1
fi
fi
markdown_to_publish="$(echo "$to_publish" |
sed -E 's%^ - (.+) => (.+)$%• \`\1\` :point_right: \`\2\`\\n%')"
workflow_url="https://circleci.com/workflow-run/$CIRCLE_WORKFLOW_ID"
if [ -z "$SLACK_WEBHOOK_URL" ]; then
echo 'Cannot post to Slack because $SLACK_WEBHOOK_URL is not set.'
exit 1
fi
cat \<<EOM | curl -X POST -H 'Content-type: application/json' --data @- $SLACK_WEBHOOK_URL
{
"text": "A package publish is waiting for approval. To approve, please visit $CIRCLE_BUILD_URL",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": ":wave: *Approval needed to publish the following to the \`$DIST_TAG\` tag*:\n\n*Packages:*\n\n$markdown_to_publish\n\n*<$workflow_url|Visit the build and click the _Confirmation_ job to approve it!>*"
}
}
]
}
EOM
- run:
name: Post to Slack on Failure
command: |
# Exit on all errors, undeclared variables and pipefailures.
set -euo pipefail
if [ -z "$SLACK_WEBHOOK_URL" ]; then
echo 'Cannot post failure to Slack because $SLACK_WEBHOOK_URL is not set.'
exit 1
fi
cat \<<EOM | curl -X POST -H 'Content-type: application/json' --data @- $SLACK_WEBHOOK_URL
{
"text": "A failure occurred during PRE-publishing. For more information, see $CIRCLE_BUILD_URL.",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": ":boom: *Failure during _PRE_-publishing dry-run!* :boom:\n\n<$CIRCLE_BUILD_URL|See the build details for more information!>"
}
}
]
}
EOM
when: on_fail
publish:
description: Publish
executor:
name: node
steps:
# We attach the workspace and just run with it, with no other guide.
# Maybe not the most reusable pattern, but also the most common case.
- attach_workspace:
at: .
- write_publish_env_script
- write_npmrc_for_npm_token
- run:
name: Publish
command: |
# Exit on all errors, undeclared variables and pipefailures.
set -euo pipefail
# If this doesn't exist, we won't have what we need to proceed.
if [ ! -x ~/scripts/apollo-setup-publish-env ]; then
echo "~/scripts/apollo-setup-publish-env is not executable."
exit 1
fi
# Fail if we don't have the right credentials to actually publish.
if [ -z "$NPM_TOKEN" ]; then
echo 'ERROR! $NPM_TOKEN is not set.'
exit 1
fi
# Load our variables what we set earlier (e.g. DIST_TAG).
source ~/scripts/apollo-setup-publish-env
echo "Publishing to npm under the $DIST_TAG tag."
# Do the real, actual publish to npm.
DEBUG=lerna npx lerna publish --dist-tag="$DIST_TAG" from-git --no-verify-access --yes
if [ -z "$SLACK_WEBHOOK_URL" ]; then
echo 'Cannot post to Slack because $SLACK_WEBHOOK_URL is not set.'
exit 1
fi
cat \<<EOM | curl -X POST -H 'Content-type: application/json' --data @- $SLACK_WEBHOOK_URL
{
"text": "New packages have been published to the \`$DIST_TAG\` tag:\n\n$MARKDOWN_VERSIONS",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": ":npm: *The following packages have been published to the \`$DIST_TAG\` tag*\n\n*Packages:*\n\n$MARKDOWN_VERSIONS"
}
}
]
}
EOM
- run:
name: Post to Slack on Failure
command: |
# Exit on all errors, undeclared variables and pipefailures.
set -euo pipefail
if [ -z "$SLACK_WEBHOOK_URL" ]; then
echo 'Cannot post failure to Slack because $SLACK_WEBHOOK_URL is not set.'
exit 1
fi
cat \<<EOM | curl -X POST -H 'Content-type: application/json' --data @- $SLACK_WEBHOOK_URL
{
"text": "A failure occurred during publishing. For more information, see $CIRCLE_BUILD_URL.",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": ":boom: *Failure during publishing!* :boom:\n\n<$CIRCLE_BUILD_URL|See the build details for more information!>"
}
}
]
}
EOM
when: on_fail