Skip to content

Commit ddbb75b

Browse files
committed
Push to the live registry after publishing
1 parent fce9dea commit ddbb75b

File tree

3 files changed

+178
-60
lines changed

3 files changed

+178
-60
lines changed

.github/workflows/pull-request.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ jobs:
1919
- lint-markdown
2020
- lint-scripts
2121
- preview
22+
- test-live-publish
2223
runs-on: ubuntu-latest
2324
steps:
2425
- uses: guibranco/github-status-action-v2@0849440ec82c5fa69b2377725b9b7852a3977e76 # v1.1.13
@@ -77,6 +78,18 @@ jobs:
7778
- name: Run Linter
7879
run: yarn run lint
7980

81+
test-live-publish:
82+
name: Test Live Registry Publish
83+
runs-on: ubuntu-latest
84+
steps:
85+
- name: Check out branch
86+
uses: actions/checkout@v4
87+
- name: Install uv
88+
uses: astral-sh/setup-uv@v5
89+
- run: uv run --with requests,pyyaml scripts/ci/push-registry.py --dry-run
90+
env:
91+
PULUMI_ACCESS_TOKEN: ${{ secrets.PULUMI_ACCESS_TOKEN }}
92+
8093
# Preview runs a registry build into a commit specific S3 bucket to preview changes.
8194
#
8295
# A link to the generated build is appended to the PR on each commit.

.github/workflows/push.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ jobs:
6464
with:
6565
name: origin-bucket-metadata
6666
path: origin-bucket-metadata.json
67+
- name: Install uv
68+
uses: astral-sh/setup-uv@v5
69+
- name: Push to the Live Registry
70+
run: uv run --with requests,pyyaml ./scripts/ci/push-registry.py
71+
env:
72+
PULUMI_ACCESS_TOKEN: ${{ secrets.PULUMI_ACCESS_TOKEN }}
6773

6874
notify:
6975
if: failure()

scripts/push-registry.py renamed to scripts/ci/push-registry.py

Lines changed: 159 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -2,122 +2,203 @@
22

33
# Can be run without dependencies with
44
#
5-
# uv run --with requests,pyyaml scripts/push-registry.py
5+
# uv run --with requests,pyyaml scripts/ci/push-registry.py
66

77
import os
88
import tempfile
99
import requests
10+
import subprocess
1011
import json
1112
import yaml
12-
import sys
13-
from logging import info, basicConfig, INFO
13+
import argparse
14+
from logging import info, warning, basicConfig, WARNING
15+
16+
basicConfig(level=WARNING)
1417

1518
yaml_db_path = "themes/default/data/registry/packages"
1619
docs_path = "themes/default/content/registry/packages"
20+
backend_url = os.getenv("PULUMI_BACKEND_URL") or "https://api.pulumi.com/api"
1721
tmpdir = tempfile.mkdtemp()
1822

19-
basicConfig(level=INFO)
23+
token = os.getenv("PULUMI_ACCESS_TOKEN")
2024

21-
info(f"Tempdir: {tmpdir}")
25+
parser = argparse.ArgumentParser(
26+
prog='push-registry',
27+
description="""Push the contents of the pulumi/registry GitHub repo to the live registry service
28+
29+
PULUMI_ACCESS_TOKEN must be set to publish.
30+
PULUMI_BACKEND_URL may be set. Defaults to "https://api.pulumi.com/api".
31+
""",
32+
epilog='This is a Pulumi internal tool - it is not intended for external use')
33+
parser.add_argument("--target", default=None, help='Only focus on one provider. This should just be the name, like "aws"')
34+
parser.add_argument("--dry-run", action='store_true', help="Don't actually publish provider, but do everything else")
2235

23-
run_script = []
36+
args = parser.parse_args()
2437

25-
target = None
26-
if len(sys.argv) >= 2:
27-
target = sys.argv[1]
38+
info(f"Tempdir: {tmpdir}")
2839

2940
publishers = {
3041
"1Password": "1Password",
31-
"airbytehq": "airbytehq",
3242
"Aviatrix": "Aviatrix",
33-
"azaurus1": "azaurus1",
34-
"castai": "castai",
35-
"celest-dev": "celest-dev",
43+
"CTFer.io": "ctfer",
3644
"Checkly": "checkly",
3745
"Christian Nunciato": "christian_nunciato",
3846
"Chronosphere": "chronosphere",
3947
"Cisco": "cisco",
40-
"civo": "civo",
41-
"constellix": "constellix",
4248
"CrowdStrike": "crowdstrike",
43-
"CTFer.io": "ctfer",
4449
"Daniel Muehlbachler-Pietrzykowski": "daniel_muehlbachler_pietrzykowski",
4550
"DataRobot, Inc.": "datarobot",
4651
"Defang": "defang",
4752
"Descope": "descope",
48-
"dirien": "dirien",
49-
"dmacvicar": "dmacvicar",
5053
"Equinix": "equinix",
5154
"EventStore": "eventstore",
55+
"Genesiscloud": "genesiscloud",
56+
"Grapl Security": "grapl_security",
57+
"Ian Wahbe": "ian_wahbe",
58+
"Impart Security": "impart_security",
59+
"Koyeb": "koyeb",
60+
"Lee Zen": "lee_zen",
61+
"Nuage-Studio": "nuage_studio",
62+
"OVHcloud": "ovhcloud",
63+
"Paul Stack": "paul_stack",
64+
"Piers Karsenbarg": "piers_karsenbarg",
65+
"Prodvana": "prodvana",
66+
"Pulumi": "pulumi",
67+
"Pulumiverse": "pulumiverse",
68+
"RedisLabs": "redislabs",
69+
"Rootly": "rootly",
70+
"Runpod": "runpod",
71+
"Symbiosis": "symbiosis",
72+
"Theo Gravity": "theo_gravity",
73+
"Three141": "three141",
74+
"Threefold": "threefold",
75+
"Twingate": "twingate",
76+
"UpCloudLtd": "upcloudltd",
77+
"Upstash": "upstash",
78+
"Vates": "vates",
79+
"Volcengine": "volcengine",
80+
"Wttech": "wttech",
81+
"Zscaler": "zscaler",
82+
"airbytehq": "airbytehq",
83+
"akeyless-community": "akeyless-community",
84+
"aptible": "aptible",
85+
"athenz": "athenz",
86+
"azaurus1": "azaurus1",
87+
"castai": "castai",
88+
"celest-dev": "celest-dev",
89+
"chainguard-dev": "chainguard-dev",
90+
"checkpointsw": "checkpointsw",
91+
"ciscodevnet": "ciscodevnet",
92+
"ciscodevnet": "ciscodevnet",
93+
"civo": "civo",
94+
"cloudfoundry-community": "cloudfoundry-community",
95+
"coder": "coder",
96+
"constellix": "constellix",
97+
"coralogix": "coralogix",
98+
"cox-automotive": "cox-automotive",
99+
"cyralinc": "cyralinc",
100+
"datadrivers": "datadrivers",
101+
"dell": "dell",
102+
"dell": "dell",
103+
"dell": "dell",
104+
"denouche": "denouche",
105+
"dirien": "dirien",
106+
"dmacvicar": "dmacvicar",
107+
"dome9": "dome9",
108+
"drfaust92": "drfaust92",
109+
"e-breuninger": "e-breuninger",
110+
"edge-center": "edge-center",
111+
"elastic": "elastic",
112+
"elastic-infra": "elastic-infra",
113+
"ferlab-ste-justine": "ferlab-ste-justine",
114+
"ferlab-ste-justine": "ferlab-ste-justine",
52115
"fivetran": "fivetran",
116+
"flexibleenginecloud": "flexibleenginecloud",
53117
"fortinetdev": "fortinetdev",
54-
"Genesiscloud": "genesiscloud",
118+
"g-core": "g-core",
119+
"glesys": "glesys",
55120
"goauthentik": "goauthentik",
56-
"Grapl Security": "grapl_security",
57121
"hashicorp": "hashicorp",
58122
"honeycombio": "honeycomb",
59-
"Ian Wahbe": "ian_wahbe",
60-
"Impart Security": "impart_security",
123+
"hpe": "hpe",
124+
"ibm-cloud": "ibm-cloud",
125+
"imperva": "imperva",
61126
"incident-io": "incident_io",
127+
"infobloxopen": "infobloxopen",
128+
"ionos-cloud": "ionos-cloud",
129+
"iterative": "iterative",
130+
"jdamata": "jdamata",
62131
"jfrog": "jfrog",
63132
"joneshf": "joneshf",
133+
"juju": "juju",
134+
"k-yomo": "k-yomo",
64135
"komminarlabs": "komminarlabs",
65136
"kong": "kong",
66-
"Koyeb": "koyeb",
67137
"labd": "labd",
138+
"lacework": "lacework",
68139
"lbrlabs": "lbrlabs",
69-
"Lee Zen": "lee_zen",
70140
"littlejo": "littlejo",
141+
"logdna": "logdna",
142+
"logzio": "logzio",
71143
"lucky3028": "lucky3028",
144+
"mastercard": "mastercard",
145+
"maxlaverse": "maxlaverse",
146+
"megaport": "megaport",
147+
"mrolla": "mrolla",
148+
"nats-io": "nats-io",
149+
"netapp": "netapp",
72150
"netlify": "netlify",
73-
"Nuage-Studio": "nuage_studio",
151+
"octopusdeploylabs": "octopusdeploylabs",
74152
"onelogin": "onelogin",
153+
"opennebula": "opennebula",
154+
"opensearch-project": "opensearch-project",
155+
"opentelekomcloud": "opentelekomcloud",
75156
"oun": "oun",
76157
"outscale": "outscale",
77-
"OVHcloud": "ovhcloud",
78-
"Paul Stack": "paul_stack",
158+
"paloaltonetworks": "paloaltonetworks",
159+
"paloaltonetworks": "paloaltonetworks",
160+
"pan-net": "pan-net",
161+
"paultyng": "paultyng",
79162
"pgEdge": "pgedge",
80-
"Piers Karsenbarg": "piers_karsenbarg",
163+
"philips-software": "philips-software",
81164
"pinecone-io": "pinecone",
82165
"planetscale": "planetscale",
83166
"port-labs": "port_labs",
84167
"prefecthq": "prefecthq",
85-
"Prodvana": "prodvana",
86168
"propelauth": "propelauth",
87-
"Pulumi": "pulumi",
88169
"pulumiverse - Marcel Arns": "pulumiverse",
89170
"pulumiverse": "pulumiverse",
90-
"Pulumiverse": "pulumiverse",
91171
"qdrant": "qdrant",
92172
"rancher": "rancher",
93-
"RedisLabs": "redislabs",
94173
"redpanda-data": "redpanda_data",
95-
"Rootly": "rootly",
96-
"Runpod": "runpod",
174+
"rollbar": "rollbar",
175+
"selectel": "selectel",
176+
"spectrocloud": "spectrocloud",
97177
"splightplatform": "splightplatform",
98178
"supabase": "supabase",
99-
"Symbiosis": "symbiosis",
179+
"sysdiglabs": "sysdiglabs",
100180
"temporalio": "temporalio",
181+
"tencentcloudstack": "tencentcloudstack",
101182
"terraform-lxd": "terraform_lxd",
102-
"Theo Gravity": "theo_gravity",
103-
"Three141": "three141",
104-
"Threefold": "threefold",
183+
"terraform-routeros": "terraform-routeros",
105184
"timescale": "timescale",
106-
"Twingate": "twingate",
107-
"UpCloudLtd": "upcloudltd",
108-
"Upstash": "upstash",
185+
"ucloud": "ucloud",
109186
"vantage-sh": "vantage-sh",
110-
"Vates": "vates",
111-
"Volcengine": "volcengine",
112-
"Wttech": "wttech",
187+
"vk-cs": "vk-cs",
188+
"vmware": "vmware",
189+
"vmware": "vmware",
190+
"vmware": "vmware",
191+
"vmware": "vmware",
192+
"vmware": "vmware",
113193
"zenduty": "zenduty",
114-
"Zscaler": "zscaler",
115194
}
116195

196+
has_failed = False
197+
117198
for filename in os.listdir(yaml_db_path):
118199
if not filename.endswith('.yaml'):
119200
continue
120-
if target and target != filename.removesuffix(".yaml"):
201+
if args.target and args.target != filename.removesuffix(".yaml"):
121202
continue
122203

123204
filepath = os.path.join(yaml_db_path, filename)
@@ -140,11 +221,11 @@
140221
if publisher_display == "DEPRECATED":
141222
continue
142223
elif publisher_display not in publishers:
143-
raise Exception(f"Missing publisher entry for {publisher_display}")
224+
raise Exception(f'Missing publisher entry for "{publisher_display}"')
144225
publisher = publishers[publisher_display]
145226

146227

147-
package_version_name = f"{source}/{publisher}/{name}@{version}"
228+
package_version_name = f"{source}/{publisher}/{name}@{version.removeprefix("v")}"
148229
info(f"Preparing command for {package_version_name}")
149230

150231
# We skip azure-native-v1, since it will (1) never be updated again and (2) isn't
@@ -155,8 +236,11 @@
155236
if name.startswith("azure-native") and name != "azure-native":
156237
continue
157238

158-
api_url = f"https://api.pulumi.com/api/registry/preview/packages/{source}/{publisher}/{name}/versions/{version}"
159-
existence_check = requests.head(api_url).status_code
239+
240+
api_url = f"{backend_url}/preview/registry/packages/{source}/{publisher}/{name}/versions/{version.removeprefix("v")}"
241+
existence_check = requests.get(api_url, headers={
242+
"Authorization": f"token {token}",
243+
}).status_code
160244

161245
if existence_check == 404:
162246
schema_file = os.path.join(tmpdir, name, "schema.json")
@@ -173,26 +257,41 @@
173257
else:
174258
schema = json.loads(response.content)
175259

176-
if "version" not in schema:
260+
261+
# We *correct* the version if it isn't present *or* if it disagrees with the
262+
# version that has been published in github.com/pulumi/registry.
263+
if "version" not in schema or schema["version"] != version.removeprefix("v"):
177264
schema["version"] = version.removeprefix("v")
178265
json.dump(schema, sf)
179266

267+
180268
cmd = [
181-
"pulumi", "package", "publish", schema_file,
182-
f"--readme={docs_path}/{name}/_index.md",
183-
f"--source={source}",
184-
f"--publisher={publisher}",
269+
"pulumi", "package", "publish",
270+
schema_file,
271+
"--readme", f"{docs_path}/{name}/_index.md",
272+
"--source", source,
273+
"--publisher", publisher,
185274
]
186275

187276
if os.path.isfile(os.path.join(docs_path, name, "_installation-configuration.md")):
188277
cmd.extend(["--installation-configuration", f"{docs_path}/{name}/installation-configuration.md"])
189278

190-
run_script.append(' '.join(cmd))
279+
if args.dry_run:
280+
print(" ".join(cmd))
281+
else:
282+
try:
283+
subprocess.run(cmd, shell=False, check=True)
284+
except subprocess.CalledProcessError:
285+
warning(f"Failed to run {" ".join(cmd)}")
286+
has_failed=True
287+
288+
os.remove(schema_file)
289+
os.removedirs(os.path.dirname(schema_file))
191290
elif existence_check == 200:
192291
info("Package already exists in the registry DB")
193292
else:
194-
info(f"Unable to check on package {package_version_name}")
195-
exit(1)
293+
warning(f"Unable to check on package {package_version_name}")
294+
has_failed=True
196295

197-
for cmd in run_script:
198-
print(cmd)
296+
if has_failed:
297+
exit(1)

0 commit comments

Comments
 (0)