Add Signed Windows Installer Workflow (#9076)

* Add Code Signing action for Windows Installer

* Clean up variable names and input

* Amend and add to documentation per PR guidelines

* Update tools/finish_release.py

Co-authored-by: Brad Warren <bmw@users.noreply.github.com>

* Update tools/finish_release.py

Amend typo

Co-authored-by: Brad Warren <bmw@users.noreply.github.com>

* Amend release script for better work flow

- SCP commands to upload and download unsigned & signed installers from CSS

* Collapse spaces

* Update tools/finish_release.py

Co-authored-by: Brad Warren <bmw@users.noreply.github.com>

* Create new windows signer function

* Update Windows Installer Script

- Update change log
- add new function for signing and document
- @TODO Streammline SSH session

* Remove Azure and Github release methods

- Methods moved to CSS
- Reduced to a ssh function that triggers the process on a CSS

* Amend Chnagelog and Remove Unneeded Deps

* Update tools/finish_release.py

Co-authored-by: Brad Warren <bmw@users.noreply.github.com>

* Add Verison Fetch Function

- For the purpose of snap releases
- Add back package to dev extras for function

* Chaneg path in ssh command

* Amend release script

* Amend the ssh command for CSS

* Update tools/finish_release.py

Co-authored-by: Brad Warren <bmw@users.noreply.github.com>

* Update script with proper path and subprocess call

* Update ssh command

* Correct typo in path

* Fix typo in path

* Update certbot/CHANGELOG.md

Co-authored-by: ohemorange <ebportnoy@gmail.com>

* Remove missed conflict text

Co-authored-by: Brad Warren <bmw@users.noreply.github.com>
Co-authored-by: ohemorange <ebportnoy@gmail.com>
This commit is contained in:
Alexis 2022-06-29 15:52:50 -07:00 committed by GitHub
parent dedbdea1d9
commit 6e1696ba32
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 43 additions and 76 deletions

View file

@ -17,6 +17,7 @@ Authors
* [Alex Halderman](https://github.com/jhalderm)
* [Alex Jordan](https://github.com/strugee)
* [Alex Zorin](https://github.com/alexzorin)
* [Alexis Hancock](https://github.com/zoracon)
* [Amir Omidi](https://github.com/aaomidi)
* [Amjad Mashaal](https://github.com/TheNavigat)
* [amplifi](https://github.com/amplifi)

View file

@ -6,7 +6,7 @@ Certbot adheres to [Semantic Versioning](https://semver.org/).
### Added
*
* Updated Windows installer to be signed and trusted in Windows
### Changed
@ -28,6 +28,7 @@ More details about these changes can be found on our GitHub repo.
* Updated Apache/NGINX TLS configs to document contents are based on ssl-config.mozilla.org
### Changed
* A change to order finalization has been made to the `acme` module and Certbot:

View file

@ -67,7 +67,6 @@ install_requires = [
dev_extras = [
'azure-devops',
'ipdb',
'PyGithub',
# poetry 1.2.0+ is required for it to pin pip, setuptools, and wheel. See
# https://github.com/python-poetry/poetry/issues/1584.
'poetry>=1.2.0a1',

View file

@ -8,17 +8,13 @@ This currently includes:
* Publishing the Windows installer in a GitHub release
Setup:
- Create a github personal access token
- https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token#creating-a-token
- You'll need repo scope
- Save the token to somewhere like ~/.ssh/githubpat.txt
- Install the snapcraft command line tool and log in to a privileged account.
- https://snapcraft.io/docs/installing-snapcraft
- Use the command `snapcraft login` to log in.
Run:
python tools/finish_release.py ~/.ssh/githubpat.txt
python tools/finish_release.py --css <URL of code signing server>
Testing:
@ -36,10 +32,10 @@ import re
import subprocess
import sys
import tempfile
import getpass
from azure.devops.connection import Connection
from zipfile import ZipFile
from azure.devops.connection import Connection
from github import Github
import requests
# Path to the root directory of the Certbot repository containing this script
@ -68,7 +64,7 @@ def parse_args(args):
# Use the file's docstring for the help text and don't let argparse reformat it.
parser = argparse.ArgumentParser(description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument('githubpat', help='path to your GitHub personal access token')
parser.add_argument('--css', type=str, required=True, help='hostname of code signing server')
group = parser.add_mutually_exclusive_group()
# We use 'store_false' and a destination related to the other type of
# artifact to cause the flag being set to disable publishing of the other
@ -80,63 +76,19 @@ def parse_args(args):
help='Skip publishing other artifacts and only publish the Windows installer')
return parser.parse_args(args)
def publish_windows(css):
"""SSH into CSS and trigger downloading Azure Pipeline assets, sign, and upload to Github
def download_azure_artifacts(tempdir):
"""Download and unzip build artifacts from Azure pipelines.
:param str path: path to a temporary directory to save the files
:returns: released certbot version number as a prefix-free string
:rtype: str
:param str css: CSS host name
"""
# Create a connection to the azure org
organization_url = 'https://dev.azure.com/certbot'
connection = Connection(base_url=organization_url)
# Find the build artifacts
build_client = connection.clients.get_build_client()
get_builds_response = build_client.get_builds('certbot', definitions='3')
build_id = get_builds_response.value[0].id
artifacts = build_client.get_artifacts('certbot', build_id)
# Save and unzip files
for filename in ('windows-installer', 'changelog'):
print("Downloading artifact %s" % filename)
url = build_client.get_artifact('certbot', build_id, filename).resource.download_url
r = requests.get(url)
r.raise_for_status()
with open(tempdir + '/' + filename + '.zip', 'wb') as f:
f.write(r.content)
print("Extracting %s" % filename)
with ZipFile(tempdir + '/' + filename + '.zip', 'r') as zipObj:
zipObj.extractall(tempdir)
version = build_client.get_build('certbot', build_id).source_branch.split('v')[1]
return version
def create_github_release(github_access_token, tempdir, version):
"""Use build artifacts to create a github release, including uploading additional assets
:param str github_access_token: string containing github access token
:param str path: path to a temporary directory where azure artifacts are located
:param str version: Certbot version number, e.g. 1.7.0
"""
# Create release
g = Github(github_access_token)
repo = g.get_user('certbot').get_repo('certbot')
release_notes = open(tempdir + '/changelog/release_notes.md', 'r').read()
print("Creating git release")
release= repo.create_git_release('v{0}'.format(version),
'Certbot {0}'.format(version),
release_notes,
draft=True)
# Upload windows installer to release
print("Uploading windows installer")
release.upload_asset(tempdir + '/windows-installer/certbot-beta-installer-win_amd64.exe')
release.update_release(release.title, release.body, draft=False)
username = getpass.getuser()
host = css
command = "ssh -t {}@{} bash /opt/certbot-misc/css/venv.sh".format(username,host)
print("SSH into CSS to trigger signing and uploading of Windows installer...")
subprocess.run(command, check=True, universal_newlines=True, shell=True)
def assert_logged_into_snapcraft():
@ -216,23 +168,37 @@ def promote_snaps(version):
print(e.stdout)
raise
def fetch_version_number():
"""Retrieve version number for release from Azure Pipelines
:returns: version number
"""
# Create a connection to the azure org
organization_url = 'https://dev.azure.com/certbot'
connection = Connection(base_url=organization_url)
# Find the build artifacts
build_client = connection.clients.get_build_client()
get_builds_response = build_client.get_builds('certbot', definitions='3')
build_id = get_builds_response.value[0].id
version = build_client.get_build('certbot', build_id).source_branch.split('v')[1]
return version
def main(args):
parsed_args = parse_args(args)
github_access_token_file = parsed_args.githubpat
github_access_token = open(github_access_token_file, 'r').read().rstrip()
css = parsed_args.css
version = fetch_version_number()
with tempfile.TemporaryDirectory() as tempdir:
version = download_azure_artifacts(tempdir)
# Once the GitHub release has been published, trying to publish it
# again fails. Publishing the snaps can be done multiple times though
# so we do that first to make it easier to run the script again later
# if something goes wrong.
if parsed_args.publish_snaps:
promote_snaps(version)
if parsed_args.publish_windows:
create_github_release(github_access_token, tempdir, version)
# Once the GitHub release has been published, trying to publish it
# again fails. Publishing the snaps can be done multiple times though
# so we do that first to make it easier to run the script again later
# if something goes wrong.
if parsed_args.publish_snaps:
promote_snaps(version)
if parsed_args.publish_windows:
publish_windows(css)
if __name__ == "__main__":
main(sys.argv[1:])