1#!/usr/bin/env python
2# Copyright 2019 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6import argparse
7import os
8import sys
9
10sys.path.append(os.path.dirname(__file__))
11
12from signing import config_factory, commands, logger, model, pipeline
13
14
15def _link_stdout_and_stderr():
16    """This script's output is entirely log messages and debugging information,
17    so there is not a useful distinction between stdout and stderr. Because some
18    subcommands this script runs output to one stream or the other, link the
19    two streams so that any buffering done by Python, or the invoker of this
20    script, does not get incorrectly interleaved.
21    """
22    stdout_fileno = sys.stdout.fileno()
23    sys.stdout.close()
24    sys.stdout = sys.stderr
25    os.dup2(sys.stderr.fileno(), stdout_fileno)
26
27
28def create_config(config_args, development):
29    """Creates the |model.CodeSignConfig| for the signing operations.
30
31    If |development| is True, the config will be modified to not require
32    restricted internal assets, nor will the products be required to match
33    specific certificate hashes.
34
35    Args:
36        config_args: List of args to expand to the config class's constructor.
37        development: Boolean indicating whether or not to modify the chosen
38            config for development testing.
39
40    Returns:
41        An instance of |model.CodeSignConfig|.
42    """
43    config_class = config_factory.get_class()
44
45    if development:
46
47        class DevelopmentCodeSignConfig(config_class):
48
49            @property
50            def codesign_requirements_basic(self):
51                return ''
52
53            @property
54            def provisioning_profile_basename(self):
55                return None
56
57            @property
58            def run_spctl_assess(self):
59                # Self-signed or ad-hoc signed signing identities won't pass
60                # spctl assessment so don't do it.
61                return False
62
63        config_class = DevelopmentCodeSignConfig
64
65    return config_class(*config_args)
66
67
68def _show_tool_versions():
69    logger.info('Showing macOS and tool versions.')
70    commands.run_command(['sw_vers'])
71    commands.run_command(['xcodebuild', '-version'])
72    commands.run_command(['xcrun', '-show-sdk-path'])
73
74
75def main():
76    parser = argparse.ArgumentParser(
77        description='Code sign and package Chrome for channel distribution.')
78    parser.add_argument(
79        '--identity',
80        required=True,
81        help='The identity to sign everything but PKGs with.')
82    parser.add_argument(
83        '--installer-identity', help='The identity to sign PKGs with.')
84    parser.add_argument(
85        '--notary-user',
86        help='The username used to authenticate to the Apple notary service.')
87    parser.add_argument(
88        '--notary-password',
89        help='The password or password reference (e.g. @keychain, see '
90        '`xcrun altool -h`) used to authenticate to the Apple notary service.')
91    parser.add_argument(
92        '--notary-asc-provider',
93        help='The ASC provider string to be used as the `--asc-provider` '
94        'argument to `xcrun altool`, to be used when --notary-user is '
95        'associated with multiple Apple developer teams. See `xcrun altool -h. '
96        'Run `iTMSTransporter -m provider -account_type itunes_connect -v off '
97        '-u USERNAME -p PASSWORD` to list valid providers.')
98    parser.add_argument(
99        '--development',
100        action='store_true',
101        help='The specified identity is for development. Certain codesign '
102        'requirements will be omitted.')
103    parser.add_argument(
104        '--input',
105        required=True,
106        help='Path to the input directory. The input directory should '
107        'contain the products to sign, as well as the Packaging directory.')
108    parser.add_argument(
109        '--output',
110        required=True,
111        help='Path to the output directory. The signed (possibly packaged) '
112        'products and installer tools will be placed here.')
113    parser.add_argument(
114        '--disable-packaging',
115        action='store_true',
116        help='Disable creating any packaging (.dmg/.pkg) specified by the '
117        'configuration.')
118    parser.add_argument(
119        '--skip-brand',
120        dest='skip_brands',
121        action='append',
122        default=[],
123        help='Causes any distribution whose brand code matches to be skipped.')
124
125    group = parser.add_mutually_exclusive_group(required=False)
126    group.add_argument(
127        '--notarize',
128        dest='notarize',
129        action='store_true',
130        help='Defaults to False. Submit the signed application and DMG to '
131        'Apple for notarization.')
132    group.add_argument('--no-notarize', dest='notarize', action='store_false')
133
134    _link_stdout_and_stderr()
135
136    parser.set_defaults(notarize=False)
137    args = parser.parse_args()
138
139    if args.notarize:
140        if not args.notary_user or not args.notary_password:
141            parser.error('The --notary-user and --notary-password arguments '
142                         'are required with --notarize.')
143
144    config = create_config(
145        (args.identity, args.installer_identity, args.notary_user,
146         args.notary_password, args.notary_asc_provider), args.development)
147    paths = model.Paths(args.input, args.output, None)
148
149    if not os.path.exists(paths.output):
150        os.mkdir(paths.output)
151
152    _show_tool_versions()
153
154    pipeline.sign_all(
155        paths,
156        config,
157        disable_packaging=args.disable_packaging,
158        do_notarization=args.notarize,
159        skip_brands=args.skip_brands)
160
161
162if __name__ == '__main__':
163    main()
164