feat: v1 tag-service and release flow for proxyd
This commit is contained in:
parent
adec715b0e
commit
7b607ea932
@ -407,16 +407,59 @@ workflows:
|
||||
docker_name: proxyd
|
||||
docker_tags: <<pipeline.git.revision>>,<<pipeline.git.branch>>
|
||||
docker_context: .
|
||||
- docker-publish:
|
||||
# - docker-publish:
|
||||
# filters:
|
||||
# tags:
|
||||
# only: /^proxyd\/v.*/
|
||||
# branches:
|
||||
# ignore: /.*/
|
||||
# name: proxyd-docker-publish
|
||||
# docker_name: proxyd
|
||||
# docker_tags: <<pipeline.git.revision>>,<<pipeline.git.branch>>
|
||||
# context:
|
||||
# - oplabs-gcr-release
|
||||
# requires:
|
||||
# - proxyd-docker-build
|
||||
|
||||
release:
|
||||
when:
|
||||
not:
|
||||
equal: [ scheduled_pipeline, << pipeline.trigger_source >> ]
|
||||
jobs:
|
||||
- hold:
|
||||
type: approval
|
||||
filters:
|
||||
tags:
|
||||
only: /^(proxyd|ufm-[a-z0-9\-]*|op-[a-z0-9\-]*)\/v.*/
|
||||
branches:
|
||||
ignore: /.*/
|
||||
- docker-build:
|
||||
name: op-ufm-docker-release
|
||||
filters:
|
||||
tags:
|
||||
only: /^op-ufm\/v.*/
|
||||
branches:
|
||||
ignore: /.*/
|
||||
docker_name: op-ufm
|
||||
docker_tags: <<pipeline.git.revision>>
|
||||
publish: true
|
||||
release: true
|
||||
context:
|
||||
- oplabs-gcr-release
|
||||
requires:
|
||||
- hold
|
||||
- docker-build:
|
||||
name: proxyd-docker-release
|
||||
filters:
|
||||
tags:
|
||||
only: /^proxyd\/v.*/
|
||||
branches:
|
||||
ignore: /.*/
|
||||
name: proxyd-docker-publish
|
||||
docker_name: proxyd
|
||||
docker_tags: <<pipeline.git.revision>>,<<pipeline.git.branch>>
|
||||
docker_tags: <<pipeline.git.revision>>
|
||||
publish: true
|
||||
release: true
|
||||
context:
|
||||
- oplabs-gcr-release
|
||||
requires:
|
||||
- proxyd-docker-build
|
||||
- hold
|
||||
|
55
.github/workflows/tag-service.yml
vendored
Normal file
55
.github/workflows/tag-service.yml
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
name: Tag Service
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
bump:
|
||||
description: 'How much to bump the version by'
|
||||
required: true
|
||||
type: choice
|
||||
options:
|
||||
- major
|
||||
- minor
|
||||
- patch
|
||||
- prerelease
|
||||
- finalize-prerelease
|
||||
service:
|
||||
description: 'Which service to release'
|
||||
required: true
|
||||
type: choice
|
||||
options:
|
||||
- op-ufm
|
||||
- proxyd
|
||||
prerelease:
|
||||
description: Increment major/minor/patch as prerelease?
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
environment: op-stack-production
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Fetch tags
|
||||
run: git fetch --tags origin --force
|
||||
- name: Setup Python 3.10
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.10"
|
||||
- name: Install deps
|
||||
run: pip install -r requirements.txt
|
||||
working-directory: ops/tag-service
|
||||
- run: ops/tag-service/tag-service.py --bump="$BUMP" --service="$SERVICE"
|
||||
env:
|
||||
INPUT_GITHUB_TOKEN: ${{ github.token }}
|
||||
BUMP: ${{ github.event.inputs.bump }}
|
||||
SERVICE: ${{ github.event.inputs.service }}
|
||||
if: ${{ github.event.inputs.prerelease == 'false' }}
|
||||
- run: ops/tag-service/tag-service.py --bump="$BUMP" --service="$SERVICE" --pre-release
|
||||
env:
|
||||
INPUT_GITHUB_TOKEN: ${{ github.token }}
|
||||
BUMP: ${{ github.event.inputs.bump }}
|
||||
SERVICE: ${{ github.event.inputs.service }}
|
||||
if: ${{ github.event.inputs.prerelease == 'true' }}
|
1
ops/tag-service/.gitignore
vendored
Normal file
1
ops/tag-service/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
venv
|
21
ops/tag-service/README.md
Normal file
21
ops/tag-service/README.md
Normal file
@ -0,0 +1,21 @@
|
||||
# Tag Service
|
||||
Tag Service is a Github action which builds new tags and applies them to services in the monorepo.
|
||||
It accepts:
|
||||
* Service name
|
||||
* Bump Amount [major, minor, patch]
|
||||
* Prerelease and Finalize-Prerelease (to add/remove `rc` versions)
|
||||
|
||||
It can be triggered from the Github Actions panel in the monorepo
|
||||
|
||||
# Tag Tool
|
||||
Tag Tool is a minimal rewrite of the Tag Service to let operators prepare and commit tags from commandline
|
||||
It accepts:
|
||||
* Service name
|
||||
* Bump Amount [major, minor, patch, prerelease, finalize-prerelease]
|
||||
|
||||
Tag Tool is meant to be run locally, and *does not* perform any write operations. Instead, it prints the git commands to console for the operator to use.
|
||||
|
||||
Additionally, a special service name "op-stack" is available, which will bump versions for `op-node`, `op-batcher` and `op-proposer` from the highest semver amongst them.
|
||||
|
||||
To run Tag Tool locally, the only dependency is `pip install semver`
|
||||
|
2
ops/tag-service/requirements.txt
Normal file
2
ops/tag-service/requirements.txt
Normal file
@ -0,0 +1,2 @@
|
||||
click==8.1.3
|
||||
semver==3.0.0-dev4
|
124
ops/tag-service/tag-service.py
Executable file
124
ops/tag-service/tag-service.py
Executable file
@ -0,0 +1,124 @@
|
||||
#!/usr/bin/env python3
|
||||
import logging.config
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
import click
|
||||
import semver
|
||||
|
||||
# Minimum version numbers for packages migrating from legacy versioning.
|
||||
MIN_VERSIONS = {
|
||||
'proxyd': '3.16.0',
|
||||
}
|
||||
|
||||
VALID_BUMPS = ('major', 'minor', 'patch', 'prerelease', 'finalize-prerelease')
|
||||
|
||||
MESSAGE_TEMPLATE = '[tag-service-release] Tag {service} at {version}'
|
||||
|
||||
LOGGING_CONFIG = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': True,
|
||||
'formatters': {
|
||||
'standard': {
|
||||
'format': '%(asctime)s [%(levelname)s]: %(message)s'
|
||||
},
|
||||
},
|
||||
'handlers': {
|
||||
'default': {
|
||||
'level': 'INFO',
|
||||
'formatter': 'standard',
|
||||
'class': 'logging.StreamHandler',
|
||||
'stream': 'ext://sys.stderr'
|
||||
},
|
||||
},
|
||||
'loggers': {
|
||||
'': {
|
||||
'handlers': ['default'],
|
||||
'level': 'INFO',
|
||||
'propagate': False
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
logging.config.dictConfig(LOGGING_CONFIG)
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.option('--bump', required=True, type=click.Choice(VALID_BUMPS))
|
||||
@click.option('--service', required=True, type=click.Choice(list(MIN_VERSIONS.keys())))
|
||||
@click.option('--pre-release/--no-pre-release', default=False)
|
||||
def tag_version(bump, service, pre_release):
|
||||
tags = subprocess.run(['git', 'tag', '--list'], capture_output=True, check=True) \
|
||||
.stdout.decode('utf-8').splitlines()
|
||||
|
||||
# Filter out tags that don't match the service name, and tags
|
||||
# for prerelease versions.
|
||||
version_pattern = f'^{service}/v\\d+\\.\\d+\\.\\d+(-rc\\.\\d+)?$'
|
||||
svc_versions = [t.replace(f'{service}/v', '') for t in tags if re.match(version_pattern, t)]
|
||||
svc_versions = sorted(svc_versions, key=lambda v: semver.Version.parse(v), reverse=True)
|
||||
|
||||
if pre_release and bump == 'prerelease':
|
||||
raise Exception('Cannot use --bump=prerelease with --pre-release')
|
||||
|
||||
if pre_release and bump == 'finalize-prerelease':
|
||||
raise Exception('Cannot use --bump=finalize-prerelease with --pre-release')
|
||||
|
||||
if len(svc_versions) == 0:
|
||||
latest_version = MIN_VERSIONS[service]
|
||||
else:
|
||||
latest_version = svc_versions[0]
|
||||
|
||||
latest_version = semver.Version.parse(latest_version)
|
||||
|
||||
log.info(f'Latest version: v{latest_version}')
|
||||
|
||||
if bump == 'major':
|
||||
bumped = latest_version.bump_major()
|
||||
elif bump == 'minor':
|
||||
bumped = latest_version.bump_minor()
|
||||
elif bump == 'patch':
|
||||
bumped = latest_version.bump_patch()
|
||||
elif bump == 'prerelease':
|
||||
bumped = latest_version.bump_prerelease()
|
||||
elif bump == 'finalize-prerelease':
|
||||
bumped = latest_version.finalize_version()
|
||||
else:
|
||||
raise Exception('Invalid bump type: {}'.format(bump))
|
||||
|
||||
if pre_release:
|
||||
bumped = bumped.bump_prerelease()
|
||||
|
||||
new_version = 'v' + str(bumped)
|
||||
new_tag = f'{service}/{new_version}'
|
||||
|
||||
log.info(f'Bumped version: {new_version}')
|
||||
|
||||
log.info('Configuring git')
|
||||
# The below env vars are set by GHA.
|
||||
gh_actor = os.environ['GITHUB_ACTOR']
|
||||
gh_token = os.environ['INPUT_GITHUB_TOKEN']
|
||||
gh_repo = os.environ['GITHUB_REPOSITORY']
|
||||
origin_url = f'https://{gh_actor}:${gh_token}@github.com/{gh_repo}.git'
|
||||
subprocess.run(['git', 'config', 'user.name', gh_actor], check=True)
|
||||
subprocess.run(['git', 'config', 'user.email', f'{gh_actor}@users.noreply.github.com'], check=True)
|
||||
subprocess.run(['git', 'remote', 'set-url', 'origin', origin_url], check=True)
|
||||
|
||||
log.info(f'Creating tag: {new_tag}')
|
||||
subprocess.run([
|
||||
'git',
|
||||
'tag',
|
||||
'-a',
|
||||
new_tag,
|
||||
'-m',
|
||||
MESSAGE_TEMPLATE.format(service=service, version=new_version)
|
||||
], check=True)
|
||||
|
||||
log.info('Pushing tag to origin')
|
||||
subprocess.run(['git', 'push', 'origin', new_tag], check=True)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
tag_version()
|
93
ops/tag-service/tag-tool.py
Normal file
93
ops/tag-service/tag-tool.py
Normal file
@ -0,0 +1,93 @@
|
||||
import argparse
|
||||
import subprocess
|
||||
import re
|
||||
import semver
|
||||
|
||||
SERVICES = [
|
||||
'ci-builder',
|
||||
'ci-builder-rust',
|
||||
'chain-mon',
|
||||
'op-node',
|
||||
'op-batcher',
|
||||
'op-challenger',
|
||||
'op-dispute-mon',
|
||||
'op-proposer',
|
||||
'da-server',
|
||||
'proxyd',
|
||||
'op-heartbeat',
|
||||
'op-contracts',
|
||||
'test',
|
||||
'op-stack', # special case for tagging op-node, op-batcher, and op-proposer together
|
||||
'op-conductor',
|
||||
]
|
||||
VERSION_PATTERN = '^{service}/v\\d+\\.\\d+\\.\\d+(-rc\\.\\d+)?$'
|
||||
GIT_TAG_COMMAND = 'git tag -a {tag} -m "{message}"'
|
||||
GIT_PUSH_COMMAND = 'git push origin {tag}'
|
||||
|
||||
def new_tag(service, version, bump):
|
||||
if bump == 'major':
|
||||
bumped = version.bump_major()
|
||||
elif bump == 'minor':
|
||||
bumped = version.bump_minor()
|
||||
elif bump == 'patch':
|
||||
bumped = version.bump_patch()
|
||||
elif bump == 'prerelease':
|
||||
bumped = version.bump_prerelease()
|
||||
elif bump == 'finalize-prerelease':
|
||||
bumped = version.finalize_version()
|
||||
else:
|
||||
raise Exception('Invalid bump type: {}'.format(bump))
|
||||
return f'{service}/v{bumped}'
|
||||
|
||||
def latest_version(service):
|
||||
# Get the list of tags from the git repository.
|
||||
tags = subprocess.run(['git', 'tag', '--list', f'{service}/v*'], capture_output=True, check=True) \
|
||||
.stdout.decode('utf-8').splitlines()
|
||||
# Filter out tags that don't match the service name, and tags for prerelease versions.
|
||||
svc_versions = sorted([t.replace(f'{service}/v', '') for t in tags])
|
||||
if len(svc_versions) == 0:
|
||||
raise Exception(f'No tags found for service: {service}')
|
||||
return svc_versions[-1]
|
||||
|
||||
def latest_among_services(services):
|
||||
latest = '0.0.0'
|
||||
for service in services:
|
||||
candidate = latest_version(service)
|
||||
if semver.compare(candidate, latest) > 0:
|
||||
latest = candidate
|
||||
return latest
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Create a new git tag for a service')
|
||||
parser.add_argument('--service', type=str, help='The name of the Service')
|
||||
parser.add_argument('--bump', type=str, help='The type of bump to apply to the version number')
|
||||
parser.add_argument('--message', type=str, help='Message to include in git tag', default='[tag-tool-release]')
|
||||
args = parser.parse_args()
|
||||
|
||||
service = args.service
|
||||
|
||||
if service == 'op-stack':
|
||||
latest = latest_among_services(['op-node', 'op-batcher', 'op-proposer'])
|
||||
else:
|
||||
latest = latest_version(service)
|
||||
|
||||
bumped = new_tag(service, semver.VersionInfo.parse(latest), args.bump)
|
||||
|
||||
print(f'latest tag: {latest}')
|
||||
print(f'new tag: {bumped}')
|
||||
print('run the following commands to create the new tag:\n')
|
||||
# special case for tagging op-node, op-batcher, and op-proposer together. All three would share the same semver
|
||||
if args.service == 'op-stack':
|
||||
print(GIT_TAG_COMMAND.format(tag=bumped.replace('op-stack', 'op-node'), message=args.message))
|
||||
print(GIT_PUSH_COMMAND.format(tag=bumped.replace('op-stack', 'op-node')))
|
||||
print(GIT_TAG_COMMAND.format(tag=bumped.replace('op-stack', 'op-batcher'), message=args.message))
|
||||
print(GIT_PUSH_COMMAND.format(tag=bumped.replace('op-stack', 'op-batcher')))
|
||||
print(GIT_TAG_COMMAND.format(tag=bumped.replace('op-stack', 'op-proposer'), message=args.message))
|
||||
print(GIT_PUSH_COMMAND.format(tag=bumped.replace('op-stack', 'op-proposer')))
|
||||
else:
|
||||
print(GIT_TAG_COMMAND.format(tag=bumped, message=args.message))
|
||||
print(GIT_PUSH_COMMAND.format(tag=bumped))
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
Loading…
Reference in New Issue
Block a user