Skip to content

Commit 9a99f1d

Browse files
committed
Merge branch 'publish-cli'
2 parents ec4e954 + 69b57a3 commit 9a99f1d

15 files changed

+435
-862
lines changed

Resources/Info.plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@
9292
<key>SUEnableAutomaticChecks</key>
9393
<true/>
9494
<key>SUFeedURL</key>
95-
<string>https://s3.amazonaws.com/builds.phere.net/GitX/development/GitX-dev.xml</string>
95+
<string>https://gitx.github.com/gitx/appcast.xml</string>
9696
<key>SUPublicDSAKeyFile</key>
9797
<string>UpdateKey.pem</string>
9898
</dict>

Resources/UpdateKey.pem

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,36 @@
11
-----BEGIN PUBLIC KEY-----
2-
MIIDOzCCAi0GByqGSM44BAEwggIgAoIBAQDLgrI0GgvtC1rRi6dRNW+N1hcieyZZ
3-
YFAjvnMpgUP6tsvc1j1V4wgK6pyuU1MCpbrMhSNccqO9D/Xx1ySpbUajn6WKeX8J
4-
FXDCi1mbq7D8jUbXhReDRcI1NbI6r2cw0Rv5wQU+RFKGwaBpyNZy434k0/zTEMlY
5-
3PzhGe3jeNOutnqc71f3LTk2wrqlVXsI4y0dl7a2JcwZyXv+/5QniKlukZIFjR5b
6-
tiwhicOtI8AsabNM1dIAVlsady+P/DwpRsTfCRY+Mo//4RU0hSE+i8Z3y0pz71Wv
7-
L89yS1GONRQMeYbRDX1ZZcfIYlMmVEcsHh94i199pUmkgdw08iTyjnlrAhUA+VTd
8-
fA3w0Yptf3eWqSZPBGCayaUCggEAFhMtxRAWnHvZW3MZTfmeDs8IhhkbSfoBtD+r
9-
2T+EUUCyYPiN4PvmF+4aNWMoWh2lsd6cpvtg5I2qo/qX1HGtQwOc2FHnMmLkUPJz
10-
kLz7nwpzgdFQ3EyZ3jzmzvNwtsYJ7y7LzkIR22jnLslSl8SiMI+gIwze/oSTEb1X
11-
o5xadOsFNiAANUSEa1/42xQEHbwfYWMJIEXTBx6GyKJDcep1bK4cFx0A+xnghIhm
12-
4oah6kYG5ns7wDEcJ/+1FcZbogif5qcTjB2UFvL77jKuqRBcI8xiALVlnAPGkC3R
13-
rT033W13tnJolae7dLy4UA3taELH+CoaoSgOk6o20ZgjJUbd0wOCAQYAAoIBAQCA
14-
E4nDRoShNyE20aUxBC8uF5MhDJE0VomcOKPmyDd3Jl4DlEogzMGAwAZKrQKcBlkB
15-
CAYaWes7ULDDA0RNABWHSI9t/brrQcOPrvRMA6wgcuA5oQcm8zVziuwXJwb+NqwG
16-
so2ysvryjlUDtHPc2jJ5hUSqkp22NeQv2iK4RoQ9pDJp5/alzH7fYgqECB45AuhA
17-
34BCKE7B2fakJ5YLQdXZOzG5O3r3azWAewqp4J34Npiqn5LSdl8FZIhMaxmi9mBa
18-
65w0CNY4/VyyvOgPqCo5zPNxCJxypsvI0IUYRWeJFkEpQLmZ0K1bpmGT1iGnT4A5
19-
5llf69yaJyoGJDoD/Rj6
2+
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
35+
OYnwI0t5XQO9V9JUcX7L
2036
-----END PUBLIC KEY-----

Scripts/build.py

Lines changed: 21 additions & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -1,181 +1,40 @@
11
#!/usr/bin/env python
2+
# encoding: utf-8
23

34
import argparse
45
import subprocess
56
import os
67

7-
import package
8-
import sign
8+
import helpers
9+
from project import Project
910

10-
app = "GitX"
11-
product = "%s.app" % (app,)
12-
label = "dev"
13-
base_version = "0.15"
14-
release_branch = "master"
1511

16-
signing_key = "Developer ID Application: Rowan James"
12+
def build(project):
13+
print "Building scheme {} ({}), please wait…".format(project.scheme(), project.current_config())
14+
helpers.xcodebuild(project.scheme(), project.workspace(), project.current_build_config(), ["build"], project.build_base_dir())
15+
print "Successfully built to {}".format(project.build_product())
1716

18-
project_root = os.getcwd()
19-
artifact_prefix = "%s-%s" % (app, label)
20-
workspace = "%s.xcworkspace" % (app,)
21-
scheme = "GitX"
22-
debug_config = "Debug"
23-
release_config = "Release"
2417

25-
agvtool = "xcrun agvtool"
18+
def clean(project):
19+
print "Cleaning scheme {} ({})".format(project.scheme(), project.current_config())
20+
helpers.xcodebuild(project.scheme(), project.workspace(), project.current_build_config(), ["clean"], project.build_base_dir())
2621

27-
updates_template_file = os.path.join(project_root, 'updates', 'GitX-dev.xml.tmpl')
28-
release_notes_file = os.path.join(project_root, 'updates', 'GitX-dev.html')
29-
updates_signing_key_file = os.path.join(project_root, 'updates', 'gitx-updates.key')
30-
updates_appcast_file = 'GitX-dev.xml'
3122

32-
pause = 3
23+
def build_cmd(args):
24+
if args.action == 'clean':
25+
project = Project(os.getcwd(), "debug")
26+
clean(project)
27+
project = Project(os.getcwd(), "release")
28+
clean(project)
29+
else:
30+
project = Project(os.getcwd(), args.action, None)
31+
build(project)
3332

34-
build_base_dir = os.path.join(project_root, "build")
35-
36-
class BuildError(RuntimeError):
37-
pass
38-
39-
def clean(args):
40-
clean_scheme(scheme, args.config)
41-
42-
def release():
43-
try:
44-
assert_clean()
45-
assert_branch(release_branch)
46-
build_number = commit_count()
47-
set_versions(base_version, build_number, "dev")
48-
49-
build_scheme(scheme, release_config)
50-
51-
build_dir = os.path.join(build_base_dir, release_config)
52-
built_product = os.path.join(build_dir, product)
53-
sign_app(built_product)
54-
55-
image_path = os.path.join(build_dir, "%s-%s.dmg" % (artifact_prefix, build_number))
56-
image_name = "%s %s" % (app, build_number)
57-
package_app(built_product, image_path, image_name)
58-
59-
prepare_release(build_number, image_path)
60-
61-
except BuildError as e:
62-
print("error: %s" % (str(e),))
63-
64-
65-
def debug():
66-
try:
67-
build_scheme(scheme, debug_config)
68-
69-
except BuildError as e:
70-
print("error: %s" % (str(e),))
71-
72-
73-
def prepare_release(build_number, image_source_path):
74-
release_dir = "release"
75-
try:
76-
os.makedirs(release_dir)
77-
except OSError:
78-
pass
79-
80-
# Tag the release
81-
tag = 'builds/%s/%s' % (base_version, build_number)
82-
subprocess.check_call(['git', 'tag', tag])
83-
84-
import appcast
85-
appcast_text = appcast.generate_appcast(image_source_path, updates_template_file, build_number, updates_signing_key_file)
86-
with open(os.path.join(release_dir, updates_appcast_file), 'w') as appcast_file:
87-
appcast_file.write(appcast_text)
88-
89-
import shutil
90-
copied_image = os.path.join(release_dir, os.path.basename(image_source_path))
91-
unversioned_image = os.path.join(release_dir, artifact_prefix + ".dmg")
92-
shutil.copyfile(image_source_path, copied_image)
93-
shutil.copyfile(image_source_path, unversioned_image)
94-
95-
publish_release_notes_file = os.path.join(release_dir, os.path.basename(release_notes_file))
96-
shutil.copyfile(release_notes_file, publish_release_notes_file)
97-
publish_release_notes_filebase, publish_release_notes_ext = os.path.splitext(publish_release_notes_file)
98-
publish_release_notes_version_file = "%s-%s%s" % (publish_release_notes_filebase, build_number, publish_release_notes_ext)
99-
shutil.copyfile(release_notes_file, publish_release_notes_version_file)
100-
101-
102-
def build(args):
103-
if args.config == "debug":
104-
debug()
105-
if args.config == "release":
106-
release()
107-
108-
109-
def assert_clean():
110-
0
111-
# status = check_string_output(["git", "status", "--porcelain", "--untracked-files=no"])
112-
# if len(status):
113-
# raise BuildError("Working copy must be clean\n%s" % status)
114-
115-
116-
def assert_branch(branch="master"):
117-
ref = check_string_output(["git", "rev-parse", "--abbrev-ref", "HEAD"])
118-
if ref != branch:
119-
raise BuildError("HEAD must be %s, but is %s" % (branch, ref))
120-
121-
122-
def build_scheme(scheme, config):
123-
xcodebuild(scheme, workspace, config, ["build"])
124-
125-
126-
def clean_scheme(scheme, config):
127-
xcodebuild(scheme, workspace, config, ["clean"])
128-
129-
130-
def commit_count():
131-
count = check_string_output(["git", "rev-list", "HEAD", "--count"])
132-
return count
133-
134-
135-
def set_versions(base_version, build_number, label):
136-
print("mvers: " + check_string_output(["agvtool", "mvers", "-terse1"]))
137-
print("vers: " + check_string_output(["agvtool", "vers", "-terse"]))
138-
marketing_version = "%s.%s %s" % (base_version, build_number, label)
139-
build_version = "%s.%s" % (base_version, build_number)
140-
subprocess.check_call(["agvtool", "new-marketing-version", marketing_version])
141-
subprocess.check_call(["agvtool", "new-version", "-all", build_version])
142-
143-
144-
def xcodebuild(scheme, workspace, config, commands):
145-
cmd = ["xcrun", "xcodebuild", "-workspace", workspace, "-scheme", scheme, "-configuration", config]
146-
cmd = cmd + commands
147-
cmd.append('BUILD_DIR=%s' % (build_base_dir))
148-
try:
149-
output = check_string_output(cmd)
150-
return output
151-
except subprocess.CalledProcessError as e:
152-
raise BuildError(str(e))
153-
154-
155-
def check_string_output(command):
156-
return subprocess.check_output(command).decode('utf-8').strip()
157-
158-
159-
def sign_app(app_path):
160-
sign.sign_everything_in_app(app_path, key=signing_key)
161-
162-
163-
def package_app(app_path, image_path, image_name):
164-
package.package(app_path, image_path, image_name)
16533

16634
if __name__ == "__main__":
167-
script_dir = os.path.dirname(os.path.abspath(__file__))
168-
16935
parser = argparse.ArgumentParser()
170-
subparsers = parser.add_subparsers()
171-
172-
parser_config = subparsers.add_parser('config')
173-
parser_config.add_argument('config', choices=['debug', 'release'])
174-
parser_config.set_defaults(func=clean)
175-
176-
parser_build = subparsers.add_parser('build')
177-
parser_build.add_argument('config', choices=['debug', 'release'])
178-
parser_build.set_defaults(func=build)
36+
parser.add_argument('action', choices=['debug', 'release', 'clean'], nargs='?', default='debug')
37+
parser.set_defaults(func=build_cmd)
17938

18039
args = parser.parse_args()
18140
args.func(args)

Scripts/helpers.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#!/usr/bin/env python
2+
3+
import subprocess
4+
import base64
5+
6+
class BuildError(RuntimeError):
7+
pass
8+
9+
10+
def sign_file(filename, signing_key_file):
11+
hashProc = subprocess.Popen(['openssl', 'dgst', '-sha1', '-binary', filename],
12+
stdout=subprocess.PIPE)
13+
hash = hashProc.communicate()[0]
14+
signProc = subprocess.Popen(['openssl', 'dgst', '-dss1', '-sign', signing_key_file],
15+
stdin=subprocess.PIPE, stdout=subprocess.PIPE)
16+
signProc.stdin.write(hash)
17+
binsig = signProc.communicate()[0]
18+
return base64.b64encode(binsig).decode()
19+
20+
21+
def check_string_output(command):
22+
return subprocess.check_output(command).decode('utf-8').strip()
23+
24+
25+
def assert_clean():
26+
status = check_string_output(["git", "status", "--porcelain", "--untracked-files=no"])
27+
if len(status):
28+
raise BuildError("Working copy must be clean\n%s" % status)
29+
30+
31+
def assert_branch(branch="master"):
32+
ref = check_string_output(["git", "rev-parse", "--abbrev-ref", "HEAD"])
33+
if ref != branch:
34+
raise BuildError("HEAD must be %s, but is %s" % (branch, ref))
35+
36+
37+
def set_version(build_version, label):
38+
marketing_version = build_version
39+
if label != None:
40+
marketing_version += " %s" % (label)
41+
subprocess.check_call(["xcrun", "agvtool", "new-marketing-version", marketing_version])
42+
subprocess.check_call(["xcrun", "agvtool", "new-version", "-all", build_version])
43+
44+
45+
def xcodebuild(scheme, workspace, config, commands, build_dir):
46+
cmd = ["xcrun", "xcodebuild", "-workspace", workspace, "-scheme", scheme, "-configuration", config]
47+
cmd = cmd + commands
48+
cmd.append('BUILD_DIR=%s' % (build_dir))
49+
try:
50+
output = check_string_output(cmd)
51+
return output
52+
except subprocess.CalledProcessError as e:
53+
raise BuildError(str(e))
54+

Scripts/package.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
import shutil
1111
import os
1212

13+
class PackageError(RuntimeError):
14+
pass
15+
1316
def package(app, bundle, name, verbose=False):
1417
appBase = os.path.dirname(app)
1518
appName = os.path.basename(app)
@@ -26,8 +29,10 @@ def package(app, bundle, name, verbose=False):
2629
'create', bundle,
2730
'-srcfolder', tmp_dir,
2831
'-volname', name]
29-
30-
subprocess.call(hdiutil)
32+
33+
ret = subprocess.call(hdiutil)
34+
# if ret != 0:
35+
# raise PackageError("Failed to package error: ")
3136
shutil.move(movedApp, app)
3237
shutil.rmtree(tmp_dir)
3338

0 commit comments

Comments
 (0)