Skip to content

Commit

Permalink
Sign/release exe and msi (open-telemetry#63)
Browse files Browse the repository at this point in the history
  • Loading branch information
jchengsfx authored Jan 21, 2021
1 parent b78152b commit 4eb7232
Show file tree
Hide file tree
Showing 9 changed files with 887 additions and 353 deletions.
4 changes: 2 additions & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -332,8 +332,8 @@ jobs:
name: Calculate checksums
command: cd dist && shasum -a 256 * > checksums.txt
- run:
name: Create Github release and upload artifacts
command: ghr -t $GITHUB_TOKEN -u $CIRCLE_PROJECT_USERNAME -r $CIRCLE_PROJECT_REPONAME --replace $CIRCLE_TAG dist/
name: Create Github pre-release and upload artifacts
command: ghr --prerelease -t $GITHUB_TOKEN -u $CIRCLE_PROJECT_USERNAME -r $CIRCLE_PROJECT_REPONAME --replace $CIRCLE_TAG dist/

publish-dev:
docker:
Expand Down
55 changes: 35 additions & 20 deletions docs/release.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,29 @@
1. You must have permissions to push tags to this repository.
1. You must be able to sign git commits/tags. Follow [this guide](
https://docs.github.com/en/github/authenticating-to-github/signing-commits)
to setup it up.
1. Access the required service account tokens for Chaperone, Splunk staging
repository, and Splunk Artifactory. Check with a team member for details.
to set it up.
1. Create a Github access token that can write to this repository by going to
[Personal Access tokens](https://github.com/settings/tokens) on Github.
Save the token as the following environment variable:
- `GITHUB_TOKEN`
1. You must have access to the required service account tokens for Chaperone,
Splunk staging repository, and Splunk Artifactory. Check with a team member
for details. Save the tokens as the following environment variables:
- `ARTIFACTORY_TOKEN`
- `CHAPERONE_TOKEN`
- `STAGING_TOKEN`
1. You must have access to the S3 bucket. Add a profile called `prod` to your
AWS CLI tool config that contains your IAM credentials to our production AWS
account. The default region does not matter because we only deal with S3
and CloudFront, which are region-less. This is generally done by adding a
section with the header `[prod]` in the file `~/.aws/credentials`.
1. Install Docker, Python 3, [pip](https://pip.pypa.io/en/stable/installing/),
and [virtualenv](https://virtualenv.pypa.io/en/latest/) on your workstation.
1. Install the required dependencies with pip in virtualenv on your workstation:
```
virtualenv venv
source venv/bin/activate
pip install -r internal/buildscripts/packaging/release/requirements.txt
$ virtualenv venv
$ source venv/bin/activate
$ pip install -r internal/buildscripts/packaging/release/requirements.txt
```

## Steps
Expand All @@ -27,21 +40,23 @@
successful.
1. Create and push the tag with the appropriate version:
```
make add-tag TAG=v1.2.3
git push --tags origin # assuming "origin" is the upstream repository and not your fork
$ make add-tag TAG=v1.2.3
$ git push --tags origin # assuming "origin" is the upstream repository and not your fork
```
1. Ensure that the build and tests for the tag are successful.
1. Ensure that the release was created and the packages were published to
[Github Releases](https://github.com/signalfx/splunk-otel-collector/releases/)
1. Run the following script in virtualenv with the respective service account
tokens to sign the packages from github and release them to Artifactory.
`STAGE` should be `test`, `beta`, or `release` (default).
1. Ensure that the `quay.io/signalfx/splunk-otel-collector:<VERSION>` image
was built and pushed.
1. Ensure that the release was created and that the packages were published to
[Github Releases](https://github.com/signalfx/splunk-otel-collector/releases/).
The packages will be unsigned, and the release will be labeled as a
"Pre-release".
1. Run the following script in virtualenv to download the packages from the
Github release, sign them, and push the signed packages to Artifactory, S3,
and back to the Github release.
```
source venv/bin/activate # if not already in virtualenv
./internal/buildscripts/packaging/release/sign_release.py --stage=STAGE --artifactory-token=ARTIFACTORY_TOKEN --chaperone-token=CHAPERONE_TOKEN --staging-token=STAGING_TOKEN
$ source venv/bin/activate # if not already in virtualenv
$ ./internal/buildscripts/packaging/release/sign_release.py
```
This may 10+ minutes to complete.

**Hint:** You can set the `ARTIFACTORY_TOKEN`, `CHAPERONE_TOKEN`, and
`STAGING_TOKEN` environment variables instead of passing the tokens via
the command line options.
This may take 10+ minutes to complete. The "Pre-release" label will then be
removed from the Github release. Run the script with `--help` for more
details and to see all available options.
Empty file.
50 changes: 50 additions & 0 deletions internal/buildscripts/packaging/release/helpers/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Copyright 2020 Splunk, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from pathlib import Path

# Artifactory
ARTIFACTORY_URL = "https://splunk.jfrog.io/artifactory"
ARTIFACTORY_API_URL = f"{ARTIFACTORY_URL}/api"
ARTIFACTORY_DEB_REPO = "otel-collector-deb"
ARTIFACTORY_DEB_REPO_URL = f"{ARTIFACTORY_URL}/{ARTIFACTORY_DEB_REPO}"
ARTIFACTORY_RPM_REPO = "otel-collector-rpm"
ARTIFACTORY_RPM_REPO_URL = f"{ARTIFACTORY_URL}/{ARTIFACTORY_RPM_REPO}"
DEFAULT_ARTIFACTORY_USERNAME = "otel-collector"

# Signing
CHAPERONE_API_URL = "https://chaperone.re.splunkdev.com/api-service"
DEFAULT_STAGING_USERNAME = "srv-otel-collector"
DEFAULT_TIMEOUT = 600
SIGN_TYPES = ("GPG", "RPM", "WIN")
SIGNED_ARTIFACTS_REPO_URL = "https://repo.splunk.com/artifactory/signed-artifacts"
STAGING_URL = "https://repo.splunk.com/artifactory"
STAGING_REPO = "otel-collector-local"
STAGING_REPO_URL = f"{STAGING_URL}/{STAGING_REPO}"

# Package/Release
REPO_DIR = Path(__file__).parent.parent.parent.parent.parent.parent.resolve()
ASSETS_BASE_DIR = REPO_DIR / "dist" / "release"
COLLECTOR_REPO = "signalfx/splunk-otel-collector"
COMPONENTS = ["deb", "rpm", "windows"]
PACKAGE_NAME = "splunk-otel-collector"
STAGES = ("release", "beta", "test", "github")
S3_BUCKET = "public-downloads--signalfuse-com"
S3_MSI_BASE_DIR = f"{PACKAGE_NAME}/msi"
CLOUDFRONT_DISTRIBUTION_ID = "EJH671JAOI5SN"

# MSI
WIX_IMAGE = "felfert/wix:latest"
WXS_PATH = "internal/buildscripts/packaging/msi/splunk-otel-collector.wxs"
MSI_CONFIG = "cmd/otelcol/config/collector/agent_config_windows.yaml"
249 changes: 249 additions & 0 deletions internal/buildscripts/packaging/release/helpers/release_args.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
# Copyright 2020 Splunk, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import argparse
import os
import sys

from .constants import (
ARTIFACTORY_URL,
COMPONENTS,
DEFAULT_ARTIFACTORY_USERNAME,
DEFAULT_STAGING_USERNAME,
DEFAULT_TIMEOUT,
STAGES,
STAGING_URL,
)


def add_signing_args(parser):
signing_args = parser.add_argument_group("Signing Credentials")
signing_args.add_argument(
"--chaperone-token",
type=str,
default=os.environ.get("CHAPERONE_TOKEN"),
metavar="CHAPERONE_TOKEN",
required=False,
help="Chaperone token. Required if the CHAPERONE_TOKEN env var is not set.",
)
signing_args.add_argument(
"--staging-user",
type=str,
default=os.environ.get("STAGING_USERNAME", DEFAULT_STAGING_USERNAME),
metavar="STAGING_USERNAME",
required=False,
help=f"""
Staging username for {STAGING_URL}.
Defaults to the STAGING_USERNAME env var if set, otherwise '{DEFAULT_STAGING_USERNAME}'.
""",
)
signing_args.add_argument(
"--staging-token",
type=str,
default=os.environ.get("STAGING_TOKEN"),
metavar="STAGING_TOKEN",
required=False,
help=f"""
Staging token for {STAGING_URL}.
Required if the STAGING_TOKEN env var is not set.
""",
)


def check_signing_args(args):
assert args.chaperone_token, f"Chaperone token not set"
assert args.staging_user, f"Staging username not set for {STAGING_URL}"
assert args.staging_token, f"Staging token not set for {STAGING_URL}"


def add_artifactory_args(parser):
artifactory_args = parser.add_argument_group("Artifactory Credentials")
artifactory_args.add_argument(
"--artifactory-user",
type=str,
default=os.environ.get("ARTIFACTORY_USERNAME", DEFAULT_ARTIFACTORY_USERNAME),
metavar="ARTIFACTORY_USERNAME",
required=False,
help=f"""
Artifactory username for {ARTIFACTORY_URL}.
Defaults to the ARTIFACTORY_USERNAME env var if set, otherwise '{DEFAULT_ARTIFACTORY_USERNAME}'.
""",
)
artifactory_args.add_argument(
"--artifactory-token",
type=str,
default=os.environ.get("ARTIFACTORY_TOKEN"),
metavar="ARTIFACTORY_TOKEN",
required=False,
help=f"""
Artifactory token for {ARTIFACTORY_URL}.
Required if the ARTIFACTORY_TOKEN env var is not set.
""",
)


def check_artifactory_args(args):
assert args.artifactory_user, f"Artifactory username not set for {ARTIFACTORY_URL}"
assert args.artifactory_token, f"Artifactory token not set for {ARTIFACTORY_URL}"


def add_github_args(parser):
github_args = parser.add_argument_group("Github Credentials")
github_args.add_argument(
"--github-token",
type=str,
default=os.environ.get("GITHUB_TOKEN"),
metavar="GITHUB_TOKEN",
required=False,
help=f"""
Personal Github token.
Required if the GITHUB_TOKEN env var is not set and STAGE is 'release'.
""",
)


def check_github_args(args):
assert args.github_token, "Github token not set"


def get_args():
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
description="Sign and release assets from Github Releases.",
)
parser.add_argument(
"--stage",
type=str,
default="release",
metavar="STAGE",
choices=STAGES,
required=False,
help=f"""
Stage for pushing the packages to Artifactory and S3.
Should be one of {STAGES}.
If STAGE is 'test', the packages will *not* be signed and *only* pushed to Artifactory and S3.
If STAGE is 'beta', the packages will be signed and *only* pushed to Artifactory and S3.
If STAGE is 'github', the packages will be signed and *only* pushed to the Github release.
If STAGE is 'release', the packages will be signed, pushed to Artifactory, S3, and Github, and the
'Pre-release' label will be removed from the Github release.
Defaults to 'release'.
""",
)
parser.add_argument(
"--no-push",
action="store_true",
default=False,
required=False,
help="Only download and sign the assets. Do not push the assets to Artifactory, S3, or Github.",
)
parser.add_argument(
"--tag",
type=str,
default=None,
metavar="TAG",
required=False,
help="Existing Github release tag (e.g. 'v1.2.3'). Defaults to the latest release tag.",
)
parser.add_argument(
"--download-only",
action="store_true",
default=False,
required=False,
help="Download assets from the Github release and exit.",
)
parser.add_argument(
"--assets-dir",
type=str,
default=None,
metavar="DIR",
required=False,
help=f"""
Directory to save the downloaded assets from the Github release.
The directory will be created if it does not exist.
This option is ignored if the '--path' option is also specified.
Defaults to 'dist/release/<RELEASE_TAG>' in the repo root directory.
Signed assets will be saved to the 'signed' sub-directory, e.g. 'dist/release/<RELEASE_TAG>/signed'.
""",
)
parser.add_argument(
"--component",
type=str,
default=[],
metavar="COMPONENT",
choices=COMPONENTS,
action="append",
required=False,
help=f"""
Only download, sign, and release the specified component from the Github release.
Should be one of {COMPONENTS}.
If COMPONENT is 'windows', the exe will be signed, and the msi will be rebuilt with the signed exe.
This option may be specified multiple times for multiple components.
The default is to perform a full sign/release for all supported components.
This option is ignored if the '--path' option is also specified.
""",
)
parser.add_argument(
"--path",
type=str,
default=[],
metavar="PATH",
action="append",
required=False,
help="""
Sign/release a local file instead of downloading the assets from the Github release.
This option may be specified multiple times for multiple files.
Only files with .deb, .rpm, or .exe extensions are supported.
If the file is .exe, the msi will also be built and signed/released.
NOTE: This option is only applicable if STAGE is 'test' or 'beta'.
""",
)
parser.add_argument(
"--timeout",
type=int,
default=DEFAULT_TIMEOUT,
metavar="TIMEOUT",
required=False,
help=f"Signing request timeout in seconds. Defaults to {DEFAULT_TIMEOUT}.",
)
parser.add_argument(
"--force",
action="store_true",
default=False,
required=False,
help="Never prompt when overwriting existing files.",
)

add_artifactory_args(parser)
add_signing_args(parser)
add_github_args(parser)

args = parser.parse_args()

if not args.component:
args.component = COMPONENTS

if args.stage != "test":
check_signing_args(args)

if not args.no_push:
if args.stage in ("beta", "release"):
check_artifactory_args(args)

if args.stage in ("github", "release"):
check_github_args(args)
if args.path:
print("The '--path' option is not supported for the 'github' or 'release' stage.")
sys.exit(1)

return args
Loading

0 comments on commit 4eb7232

Please sign in to comment.