|
1 | 1 | import argparse |
2 | 2 | import glob |
3 | 3 | import os |
| 4 | +import shlex |
| 5 | +import shutil |
4 | 6 | import subprocess |
| 7 | +import tempfile |
5 | 8 | from enum import Enum |
6 | 9 | from typing import Dict |
7 | 10 | from typing import List |
@@ -338,34 +341,62 @@ def build_package( |
338 | 341 | postgres_version: str, |
339 | 342 | input_output_parameters: InputOutputParameters, |
340 | 343 | is_test: bool = False, |
341 | | - container_gh_token: str = "", |
| 344 | + actor_name: str = "", |
| 345 | + actor_email: str = "", |
342 | 346 | ): |
343 | 347 | docker_image_name = "packaging" if not is_test else "packaging-test" |
344 | 348 | postgres_extension = "all" if postgres_version == "all" else f"pg{postgres_version}" |
345 | | - # Packaging containers call GET /user and GET /user/emails via determine_name/determine_email. |
346 | | - # Those endpoints require a user-scoped token (PAT). GitHub App installation tokens are |
347 | | - # repository-scoped and will receive a 403 for user endpoints, so a separate PAT token is |
348 | | - # used for the container when provided. |
349 | | - os.environ["GITHUB_TOKEN"] = container_gh_token if container_gh_token else github_token |
| 349 | + os.environ["GITHUB_TOKEN"] = github_token |
350 | 350 | os.environ["CONTAINER_BUILD_RUN_ENABLED"] = "true" |
351 | 351 | if not os.path.exists(input_output_parameters.output_dir): |
352 | 352 | os.makedirs(input_output_parameters.output_dir) |
353 | 353 |
|
| 354 | + # When an actor name and email are provided (e.g. when running under a GitHub App |
| 355 | + # installation token), we shadow the /scripts/determine_name and |
| 356 | + # /scripts/determine_email scripts inside the container with tiny override scripts |
| 357 | + # that just echo the supplied values. This avoids the GET /user and GET /user/emails |
| 358 | + # API calls that those scripts make, which fail with 403 when the token is a GitHub |
| 359 | + # App installation token (not a user-scoped PAT). |
| 360 | + actor_tmpdir = None |
| 361 | + actor_mounts = "" |
| 362 | + if actor_name and actor_email: |
| 363 | + actor_tmpdir = tempfile.mkdtemp(prefix="citus_pkg_actor_") |
| 364 | + for script_name, value in [ |
| 365 | + ("determine_name", actor_name), |
| 366 | + ("determine_email", actor_email), |
| 367 | + ]: |
| 368 | + script_path = os.path.join(actor_tmpdir, script_name) |
| 369 | + with open(script_path, "w", encoding="utf-8") as f: |
| 370 | + f.write(f"#!/bin/bash\nprintf '%s\\n' {shlex.quote(value)}\n") |
| 371 | + os.chmod(script_path, 0o755) |
| 372 | + actor_mounts = ( |
| 373 | + f"-v {actor_tmpdir}/determine_name:/scripts/determine_name:ro " |
| 374 | + f"-v {actor_tmpdir}/determine_email:/scripts/determine_email:ro " |
| 375 | + ) |
| 376 | + |
354 | 377 | docker_command = ( |
355 | | - f"docker run --rm -v {input_output_parameters.output_dir}:/packages -v " |
| 378 | + f"docker run --rm {actor_mounts}" |
| 379 | + f"-v {input_output_parameters.output_dir}:/packages -v " |
356 | 380 | f"{input_output_parameters.input_files_dir}:/buildfiles:ro " |
357 | 381 | f"-e GITHUB_TOKEN -e PACKAGE_ENCRYPTION_KEY -e UNENCRYPTED_PACKAGE -e CONTAINER_BUILD_RUN_ENABLED " |
358 | 382 | f"-e MSRUSTUP_PAT -e CRATES_IO_MIRROR_FEED_TOKEN -e INSTALL_RUST -e CI " |
359 | 383 | f"citus/{docker_image_name}:{docker_platform}-{postgres_extension} {build_type.name}" |
360 | 384 | ) |
361 | 385 |
|
362 | 386 | print(f"Executing docker command: {docker_command}") |
363 | | - output = run_with_output(docker_command, text=True) |
| 387 | + try: |
| 388 | + output = run_with_output(docker_command, text=True) |
| 389 | + finally: |
| 390 | + if actor_tmpdir: |
| 391 | + shutil.rmtree(actor_tmpdir, ignore_errors=True) |
364 | 392 |
|
365 | | - if output.stdout: |
366 | | - print("Output:" + output.stdout) |
367 | 393 | if output.returncode != 0: |
368 | | - raise ValueError(output.stderr) |
| 394 | + raise ValueError( |
| 395 | + "Docker command failed.\n" |
| 396 | + f"Command: {docker_command}\n" |
| 397 | + f"Exit code: {output.returncode}\n" |
| 398 | + f"--- combined output (stdout+stderr) ---\n{output.stdout}\n" |
| 399 | + ) |
369 | 400 |
|
370 | 401 | if input_output_parameters.output_validation: |
371 | 402 | validate_output( |
@@ -405,7 +436,8 @@ def build_packages( |
405 | 436 | signing_credentials: SigningCredentials, |
406 | 437 | input_output_parameters: InputOutputParameters, |
407 | 438 | is_test: bool = False, |
408 | | - container_gh_token: str = "", |
| 439 | + actor_name: str = "", |
| 440 | + actor_email: str = "", |
409 | 441 | ) -> None: |
410 | 442 | os_name, os_version = decode_os_and_release(platform) |
411 | 443 | release_versions, nightly_versions = get_postgres_versions( |
@@ -454,7 +486,8 @@ def build_packages( |
454 | 486 | postgres_docker_extension, |
455 | 487 | input_output_parameters, |
456 | 488 | is_test, |
457 | | - container_gh_token, |
| 489 | + actor_name, |
| 490 | + actor_email, |
458 | 491 | ) |
459 | 492 | print( |
460 | 493 | f"Package build for {os_name}-{os_version} for postgres {postgres_docker_extension} finished " |
@@ -528,13 +561,19 @@ def tear_release_stage_from_package_version(package_version: str) -> str: |
528 | 561 | parser = argparse.ArgumentParser() |
529 | 562 | parser.add_argument("--gh_token", required=True) |
530 | 563 | parser.add_argument( |
531 | | - "--container_gh_token", |
| 564 | + "--actor_name", |
| 565 | + required=False, |
| 566 | + default="", |
| 567 | + help="Committer name written into package changelogs. When set together with " |
| 568 | + "--actor_email, the packaging container's determine_name and determine_email " |
| 569 | + "scripts are overridden so that GET /user is never called. This allows " |
| 570 | + "GitHub App installation tokens to be used without a PAT.", |
| 571 | + ) |
| 572 | + parser.add_argument( |
| 573 | + "--actor_email", |
532 | 574 | required=False, |
533 | 575 | default="", |
534 | | - help="Token used as GITHUB_TOKEN inside packaging containers. " |
535 | | - "Defaults to --gh_token when not set. Must be a user-scoped token " |
536 | | - "(PAT) because the packaging scripts call GET /user and GET /user/emails, " |
537 | | - "which are not accessible with GitHub App installation tokens.", |
| 576 | + help="Committer email written into package changelogs. See --actor_name.", |
538 | 577 | ) |
539 | 578 | parser.add_argument("--platform", required=False, choices=platform_names()) |
540 | 579 | parser.add_argument( |
@@ -567,5 +606,6 @@ def tear_release_stage_from_package_version(package_version: str) -> str: |
567 | 606 | sign_credentials, |
568 | 607 | io_parameters, |
569 | 608 | args.is_test, |
570 | | - args.container_gh_token, |
| 609 | + args.actor_name, |
| 610 | + args.actor_email, |
571 | 611 | ) |
0 commit comments