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_name: proxyd
|
||||||
docker_tags: <<pipeline.git.revision>>,<<pipeline.git.branch>>
|
docker_tags: <<pipeline.git.revision>>,<<pipeline.git.branch>>
|
||||||
docker_context: .
|
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:
|
filters:
|
||||||
tags:
|
tags:
|
||||||
only: /^proxyd\/v.*/
|
only: /^proxyd\/v.*/
|
||||||
branches:
|
branches:
|
||||||
ignore: /.*/
|
ignore: /.*/
|
||||||
name: proxyd-docker-publish
|
|
||||||
docker_name: proxyd
|
docker_name: proxyd
|
||||||
docker_tags: <<pipeline.git.revision>>,<<pipeline.git.branch>>
|
docker_tags: <<pipeline.git.revision>>
|
||||||
|
publish: true
|
||||||
|
release: true
|
||||||
context:
|
context:
|
||||||
- oplabs-gcr-release
|
- oplabs-gcr-release
|
||||||
requires:
|
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