1 Commits

Author SHA1 Message Date
9b3653c15d WIP
All checks were successful
run tests / check (push) Successful in 15s
run tests / release (push) Successful in 13s
2025-12-05 21:22:56 +01:00
18 changed files with 48 additions and 1229 deletions

View File

@@ -1 +0,0 @@
version_descriptor: version.txt

View File

@@ -12,34 +12,7 @@ jobs:
shell: nix develop --command bash -- {0} shell: nix develop --command bash -- {0}
run: just test-python run: just test-python
test-declare-default: test-actions:
runs-on: action-runner
steps:
- uses: actions/checkout@v4
- uses: ./declare
with:
configure_runner_environment: false
- run: set -u; echo "$RELEASE_PROJECT_CURRENT_VERSION"
test-declare-with-release-yaml:
runs-on: action-runner
steps:
- uses: actions/checkout@v4
# skip login locally
- if: github.repository == 'actions/release'
uses: https://gitea.puzzleyou.net/actions/configure-runner-environment@master
- uses: ./release
with:
dry_run: true
configure_runner_environment: false
- run: set -u; echo "$RELEASE_PROJECT_CURRENT_VERSION"
test-declare-directly:
runs-on: action-runner runs-on: action-runner
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@@ -65,7 +38,6 @@ jobs:
with: with:
type: tarball type: tarball
filename: "test-assets/foo.tar.gz" filename: "test-assets/foo.tar.gz"
package_name: foo
- uses: ./add-artefact - uses: ./add-artefact
with: with:
@@ -91,12 +63,4 @@ jobs:
repository: europe-docker.hetzner.cloud/puzzleyou/helm repository: europe-docker.hetzner.cloud/puzzleyou/helm
condition: always condition: always
- name: ensure that environment variables are set - uses: ./dump
run: env | grep "RELEASE_IMAGE_LOCAL_NAME_SOME_DOCKER_IMAGE"
- name: dump release environment variables
run: env | grep "RELEASE_"
- name: dump project description
uses: ./dump

View File

@@ -14,5 +14,8 @@ jobs:
runs-on: action-runner runs-on: action-runner
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: ./declare # TODO single action - uses: ./declare
- uses: ./release with:
version_descriptor: version.txt
- uses: ./dump

View File

@@ -21,11 +21,6 @@ inputs:
description: "required for tarball, sdist" description: "required for tarball, sdist"
default: "" default: ""
package_name:
required: false
description: "required for tarball"
default: ""
pattern: pattern:
required: false required: false
description: "required for wheel" description: "required for wheel"
@@ -46,15 +41,13 @@ runs:
steps: steps:
- name: add artefact - name: add artefact
run: | run: |
nix run ${{ github.action_path }} -- \ nix run . -- \
add-artefact \ add-artefact \
--state "${RELEASE_ACTION_STATEFILE}" \ --state "${RELEASE_ACTION_STATEFILE}" \
--artefact-type "${{ inputs.type }}" \ --artefact-type "${{ inputs.type }}" \
--artefact-repository "${{ inputs.repository }}" \ --artefact-repository "${{ inputs.repository }}" \
--artefact-name "${{ inputs.name }}" \ --artefact-name "${{ inputs.name }}" \
--artefact-filename "${{ inputs.filename }}" \ --artefact-filename "${{ inputs.filename }}" \
--artefact-package-name "${{ inputs.package_name }}" \
--artefact-pattern "${{ inputs.pattern }}" \ --artefact-pattern "${{ inputs.pattern }}" \
--artefact-directory "${{ inputs.directory }}" \ --artefact-directory "${{ inputs.directory }}" \
--version-descriptor "${{ inputs.version_descriptor }}" \ --version-descriptor "${{ inputs.version_descriptor }}"
--write-env-vars-to-filename "$GITHUB_ENV"

View File

@@ -38,7 +38,7 @@ runs:
steps: steps:
- name: add deployment - name: add deployment
run: | run: |
nix run ${{ github.action_path }} -- \ nix run . -- \
add-deployment \ add-deployment \
--state "${RELEASE_ACTION_STATEFILE}" \ --state "${RELEASE_ACTION_STATEFILE}" \
--deployment-type "${{ inputs.type }}" \ --deployment-type "${{ inputs.type }}" \
@@ -46,5 +46,4 @@ runs:
--deployment-condition "${{ inputs.condition }}" \ --deployment-condition "${{ inputs.condition }}" \
--deployment-image-paths "${{ inputs.image_paths }}" \ --deployment-image-paths "${{ inputs.image_paths }}" \
--deployment-namespace "${{ inputs.namespace }}" \ --deployment-namespace "${{ inputs.namespace }}" \
--deployment-repository "${{ inputs.repository }}" \ --deployment-repository "${{ inputs.repository }}"
--write-env-vars-to-filename "$GITHUB_ENV"

View File

@@ -2,16 +2,8 @@ name: "declare project for release"
description: "start release configuration by declaring a project" description: "start release configuration by declaring a project"
inputs: inputs:
filename:
required: false
default: ".gitea/release.yaml"
description: |
location of release.yaml file. will be ignored if version_descriptor is
set.
version_descriptor: version_descriptor:
required: false required: true
default: ""
configure_runner_environment: configure_runner_environment:
required: false required: false
@@ -21,10 +13,6 @@ inputs:
required: false required: false
default: "${{ github.repository }}" default: "${{ github.repository }}"
gitea_instance:
required: false
default: "https://gitea.puzzleyou.net"
artefact_type: artefact_type:
required: false required: false
description: "known types: oci_image, tarball, wheel, sdist, npm" description: "known types: oci_image, tarball, wheel, sdist, npm"
@@ -44,11 +32,6 @@ inputs:
description: "required for tarball, sdist" description: "required for tarball, sdist"
default: "" default: ""
artefact_package_name:
required: false
description: "required for tarball"
default: ""
artefact_pattern: artefact_pattern:
required: false required: false
description: "required for wheel" description: "required for wheel"
@@ -104,23 +87,14 @@ runs:
- name: init action state - name: init action state
run: | run: |
if [[ ! -z "${RELEASE_PROJECT_CURRENT_VERSION}" ]]; then
echo "already set up."
exit 0
fi
RELEASE_ACTION_STATEFILE=$(mktemp) RELEASE_ACTION_STATEFILE=$(mktemp)
echo "[release] statefile: $RELEASE_ACTION_STATEFILE" echo "[release] statefile: $RELEASE_ACTION_STATEFILE"
echo "RELEASE_ACTION_STATEFILE="$RELEASE_ACTION_STATEFILE"" \ echo "RELEASE_ACTION_STATEFILE="$RELEASE_ACTION_STATEFILE"" \
>> "$GITHUB_ENV" >> "$GITHUB_ENV"
- name: declare release project - name: declare release project
if: inputs.version_descriptor == ''
run: | run: |
if [[ ! -z "${RELEASE_PROJECT_CURRENT_VERSION}" ]]; then # TODO get current release version
echo "already set up."
exit 0
fi
if [[ "${{ github.ref_name }}" == "master" || "${{ github.ref_name }}" == "main" ]]; then if [[ "${{ github.ref_name }}" == "master" || "${{ github.ref_name }}" == "main" ]]; then
IS_PRE_RELEASE="0" IS_PRE_RELEASE="0"
@@ -128,41 +102,10 @@ runs:
IS_PRE_RELEASE="1" IS_PRE_RELEASE="1"
fi fi
nix run ${{ github.action_path }} -- \ nix run . -- \
declare \
--state "${RELEASE_ACTION_STATEFILE}" \
--release-yaml-filename "${{ inputs.filename }}" \
--gitea-instance "${{ inputs.gitea_instance }}" \
--release-repository-name "${{ inputs.repository }}" \
--release-ref-name "${{ github.ref_name }}" \
--release-run-number "${{ github.run_number }}" \
--release-commit-sha "${{ github.sha }}" \
--is-pre-release "${IS_PRE_RELEASE}"
nix run ${{ github.action_path }} -- \
dump \
--state "${RELEASE_ACTION_STATEFILE}" \
--write-env-vars-to-filename "$GITHUB_ENV"
- name: declare release project
if: inputs.version_descriptor != ''
run: |
if [[ ! -z "${RELEASE_PROJECT_CURRENT_VERSION}" ]]; then
echo "already set up."
exit 0
fi
if [[ "${{ github.ref_name }}" == "master" || "${{ github.ref_name }}" == "main" ]]; then
IS_PRE_RELEASE="0"
else
IS_PRE_RELEASE="1"
fi
nix run ${{ github.action_path }} -- \
declare \ declare \
--state "${RELEASE_ACTION_STATEFILE}" \ --state "${RELEASE_ACTION_STATEFILE}" \
--version-descriptor "${{ inputs.version_descriptor }}" \ --version-descriptor "${{ inputs.version_descriptor }}" \
--gitea-instance "${{ inputs.gitea_instance }}" \
--release-repository-name "${{ inputs.repository }}" \ --release-repository-name "${{ inputs.repository }}" \
--release-ref-name "${{ github.ref_name }}" \ --release-ref-name "${{ github.ref_name }}" \
--release-run-number "${{ github.run_number }}" \ --release-run-number "${{ github.run_number }}" \
@@ -170,21 +113,20 @@ runs:
--is-pre-release "${IS_PRE_RELEASE}" \ --is-pre-release "${IS_PRE_RELEASE}" \
if [[ ! -z "${{ inputs.artefact_type }}" ]]; then if [[ ! -z "${{ inputs.artefact_type }}" ]]; then
nix run ${{ github.action_path }} -- \ nix run . -- \
add-artefact \ add-artefact \
--state "${RELEASE_ACTION_STATEFILE}" \ --state "${RELEASE_ACTION_STATEFILE}" \
--artefact-type "${{ inputs.artefact_type }}" \ --artefact-type "${{ inputs.artefact_type }}" \
--artefact-repository "${{ inputs.artefact_repository }}" \ --artefact-repository "${{ inputs.artefact_repository }}" \
--artefact-name "${{ inputs.artefact_name }}" \ --artefact-name "${{ inputs.artefact_name }}" \
--artefact-filename "${{ inputs.artefact_filename }}" \ --artefact-filename "${{ inputs.artefact_filename }}" \
--artefact-package-name "${{ inputs.artefact_package_name }}" \
--artefact-pattern "${{ inputs.artefact_pattern }}" \ --artefact-pattern "${{ inputs.artefact_pattern }}" \
--artefact-directory "${{ inputs.artefact_directory }}" \ --artefact-directory "${{ inputs.artefact_directory }}" \
--version-descriptor "${{ inputs.artefact_version_descriptor }}" --version-descriptor "${{ inputs.artefact_version_descriptor }}"
fi fi
if [[ ! -z "${{ inputs.deployment_type }}" ]]; then if [[ ! -z "${{ inputs.deployment_type }}" ]]; then
nix run ${{ github.action_path }} -- \ nix run . -- \
add-deployment \ add-deployment \
--state "${RELEASE_ACTION_STATEFILE}" \ --state "${RELEASE_ACTION_STATEFILE}" \
--deployment-type "${{ inputs.deployment_type }}" \ --deployment-type "${{ inputs.deployment_type }}" \
@@ -194,8 +136,3 @@ runs:
--deployment-namespace "${{ inputs.deployment_namespace }}" \ --deployment-namespace "${{ inputs.deployment_namespace }}" \
--deployment-repository "${{ inputs.deployment_repository }}" --deployment-repository "${{ inputs.deployment_repository }}"
fi fi
nix run ${{ github.action_path }} -- \
dump \
--state "${RELEASE_ACTION_STATEFILE}" \
--write-env-vars-to-filename "$GITHUB_ENV"

View File

@@ -6,6 +6,6 @@ runs:
steps: steps:
- name: dump project description - name: dump project description
run: | run: |
nix run ${{ github.action_path }} -- \ nix run . -- \
dump \ dump \
--state "${RELEASE_ACTION_STATEFILE}" --state "${RELEASE_ACTION_STATEFILE}"

View File

@@ -16,8 +16,6 @@
flake8 flake8
semver semver
toml toml
requests
pyyaml
]); ]);
pythonPackage = pkgs.python3Packages.buildPythonPackage { pythonPackage = pkgs.python3Packages.buildPythonPackage {
@@ -41,9 +39,7 @@
libraries = with pkgs.python3Packages; [ libraries = with pkgs.python3Packages; [
semver # TODO move to setup.py? semver # TODO move to setup.py?
toml toml
requests
pythonPackage pythonPackage
pyyaml
]; ];
} }
(builtins.readFile ./src/main.py) (builtins.readFile ./src/main.py)

View File

@@ -10,18 +10,6 @@ test-workflows:
--image "-self-hosted" \ --image "-self-hosted" \
--event pull_request \ --event pull_request \
--workflows ./.gitea/workflows/check.yaml \ --workflows ./.gitea/workflows/check.yaml \
--job test-declare-with-release-yaml --job test-actions
act_runner exec \
--image "-self-hosted" \
--event pull_request \
--workflows ./.gitea/workflows/check.yaml \
--job test-declare-directly
act_runner exec \
--image "-self-hosted" \
--event pull_request \
--workflows ./.gitea/workflows/check.yaml \
--job test-declare-default
# --image "europe-docker.pkg.dev/puzzle-and-play/docker/action-runner-job:latest" \ # --image "europe-docker.pkg.dev/puzzle-and-play/docker/action-runner-job:latest" \

View File

@@ -1,41 +0,0 @@
name: "release project"
description: "publish artifacte, update deployments and create gitea release"
inputs:
dry_run:
required: false
description: "do not change external state"
default: false
configure_runner_environment:
required: false
default: true
runs:
using: composite
steps:
- name: declare project if neccessary
uses: ./declare
with:
configure_runner_environment: ${{ inputs.configure_runner_environment }}
- name: publish artefacts
run: |
nix run ${{ github.action_path }} -- \
publish-artefacts \
--state "${RELEASE_ACTION_STATEFILE}" \
--dry-run "${{ inputs.dry_run }}"
- name: update deployments
run: |
nix run ${{ github.action_path }} -- \
update-deployments \
--state "${RELEASE_ACTION_STATEFILE}" \
--dry-run "${{ inputs.dry_run }}"
- name: create release
run: |
nix run ${{ github.action_path }} -- \
create-release \
--state "${RELEASE_ACTION_STATEFILE}" \
--dry-run "${{ inputs.dry_run }}"

View File

@@ -1,19 +1,13 @@
import pickle import pickle
import re
from argparse import ArgumentParser from argparse import ArgumentParser
from dataclasses import replace from dataclasses import replace
from typing import Optional from typing import Optional
import yaml
from release import versioning from release import versioning
from release.context import ReleaseContext from release.context import ReleaseContext
from release.project import (ArtefactDescription, DeploymentCondition, from release.project import (ArtefactDescription, DeploymentCondition,
DeploymentDescription, HelmRelease, Npm, OciImage, DeploymentDescription, HelmRelease, Npm, OciImage,
ProjectDescription, Sdist, Tarball, Wheel, ProjectDescription, Sdist, Tarball, Wheel)
parse_project_description)
from release.release import (create_release, publish_artefacts,
update_deployments)
def load_project_description(filename) -> ProjectDescription: def load_project_description(filename) -> ProjectDescription:
@@ -31,14 +25,12 @@ def make_project_description(
return ProjectDescription(version_descriptor=version_descriptor_filename) return ProjectDescription(version_descriptor=version_descriptor_filename)
def make_context(gitea_instance: str, def make_context(repository_name: str,
repository_name: str,
ref_name: str, ref_name: str,
run_number: str, run_number: str,
commit_sha: str, commit_sha: str,
is_pre_release: bool) -> ReleaseContext: is_pre_release: bool) -> ReleaseContext:
return ReleaseContext(gitea_instance=gitea_instance, return ReleaseContext(repository_name=repository_name,
repository_name=repository_name,
ref_name=ref_name, ref_name=ref_name,
run_number=run_number, run_number=run_number,
commit_sha=commit_sha, commit_sha=commit_sha,
@@ -47,10 +39,8 @@ def make_context(gitea_instance: str,
def dump_project_description(project_description: ProjectDescription): def dump_project_description(project_description: ProjectDescription):
print('version_descriptor: %s' % project_description.version_descriptor) print('version_descriptor: %s' % project_description.version_descriptor)
print('project version: %s' % project_description.project_version) print('project version: %s' %
print('planned version: %s' % project_description.planned_version) versioning.use_any(project_description.version_descriptor).version)
print('is already released: %s' % project_description.is_released)
print('')
print('artefacts:') print('artefacts:')
for artefact in project_description.artefacts: for artefact in project_description.artefacts:
@@ -59,35 +49,21 @@ def dump_project_description(project_description: ProjectDescription):
artefact.version_descriptor artefact.version_descriptor
or project_description.version_descriptor) or project_description.version_descriptor)
release_info = generated.make_release_info(
project_description.context,
project_description.planned_version)
if isinstance(generated, OciImage): if isinstance(generated, OciImage):
print(' - oci image: %s' % generated.name) print(' - oci image: %s' % generated.name)
print(' repository: %s' % generated.repository) print(' repository: %s' % generated.repository)
print(' local tag: %s' % release_info.local_tag)
print(' local name: %s' % release_info.local_full_name)
print(' remote names:')
for name in release_info.remote_full_names:
print(' - %s' % name)
print(' docker tags: %s' % ", ".join(release_info.tags))
elif isinstance(generated, Tarball): elif isinstance(generated, Tarball):
print(' - tarball: %s' % generated.filename) print(' - tarball: %s' % generated.filename)
print(' package name: %s' % generated.package_name)
print(' repository: %s' % generated.repository) print(' repository: %s' % generated.repository)
print(' release version name: %s' % release_info.version_str)
elif isinstance(generated, Wheel): elif isinstance(generated, Wheel):
print(' - wheel: %s' % generated.pattern) print(' - wheel: %s' % generated.pattern)
print(' repository: %s' % generated.repository) print(' repository: %s' % generated.repository)
print(' release version name: %s' % release_info.version_str)
elif isinstance(generated, Sdist): elif isinstance(generated, Sdist):
print(' - sdist: %s' % generated.filename) print(' - sdist: %s' % generated.filename)
print(' repository: %s' % generated.repository) print(' repository: %s' % generated.repository)
print(' release version name: %s' % release_info.version_str)
elif isinstance(generated, Npm): elif isinstance(generated, Npm):
print(' - npm: %s' % generated.directory) print(' - npm: %s' % generated.directory)
@@ -123,33 +99,17 @@ def dump_project_description(project_description: ProjectDescription):
print('context:') print('context:')
context = project_description.context context = project_description.context
print(' gitea instance: %s' % context.gitea_instance)
print(' repository name: %s' % context.repository_name) print(' repository name: %s' % context.repository_name)
print(' ref name: %s' % context.ref_name) print(' ref name: %s' % context.ref_name)
print(' run number: %s' % context.run_number) print(' run number: %s' % context.run_number)
print(' commit sha: %s' % context.commit_sha) print(' commit sha: %s' % context.commit_sha)
print(' is pre-release: %s' % context.is_pre_release) print(' is pre-release: %s' % context.is_pre_release)
print('')
print('release info:')
release_info = project_description.release_info
print(' title: %s' % release_info.gitea_release_title)
print(' description: %s' % release_info.gitea_release_description)
print(' is prerelease: %s' % release_info.gitea_is_prerelease)
print(' git target commitish: %s' % release_info.gitea_git_commitish)
print(' git tags: %s' % ', '.join(release_info.git_tags))
print('')
print('environment variables:')
for key, value in project_description.environment_variables.items():
print(' %s: %s' % (key, value))
def make_artefact(type: str, def make_artefact(type: str,
repository: str, repository: str,
name: str, name: str,
filename: str, filename: str,
package_name: str,
pattern: str, pattern: str,
directory: str, directory: str,
version_descriptor) -> ArtefactDescription: version_descriptor) -> ArtefactDescription:
@@ -164,10 +124,7 @@ def make_artefact(type: str,
elif type == 'tarball': elif type == 'tarball':
assert filename is not None assert filename is not None
assert package_name is not None generated = Tarball(filename=filename, **maybe_repository)
generated = Tarball(filename=filename,
package_name=package_name,
**maybe_repository)
elif type == 'wheel': elif type == 'wheel':
assert pattern is not None assert pattern is not None
@@ -234,17 +191,7 @@ def make_deployment(type: str,
if __name__ == '__main__': if __name__ == '__main__':
parser = ArgumentParser() parser = ArgumentParser()
parser.add_argument('action', choices=[ parser.add_argument('action', choices=[
# TODO missing: adjust version (development) 'declare', 'add-artefact', 'add-deployment', 'dump'])
# TODO missing: check if release already exists
'declare',
'check',
'add-artefact',
'add-deployment',
'publish-artefacts',
'update-deployments',
'create-release',
'dump'
])
def nullable_string(val): def nullable_string(val):
if not val: if not val:
@@ -254,32 +201,28 @@ if __name__ == '__main__':
def space_separated(val): def space_separated(val):
return list(filter(lambda it: it != '', (val or '').split(' '))) return list(filter(lambda it: it != '', (val or '').split(' ')))
def true_or_false(val): def zero_or_one(val):
if val == '0' or val == 'false': if val == '0':
return False return False
elif val == '1' or val == 'true': elif val == '1':
return True return True
else: else:
raise Exception('flag can be "0" or "1". got: %s' % val) raise Exception('flag can be "0" or "1". got: %s' % val)
parser.add_argument('--state', required=True) parser.add_argument('--state', required=True)
parser.add_argument('--version-descriptor', type=nullable_string) parser.add_argument('--version-descriptor', type=nullable_string)
parser.add_argument('--release-yaml-filename', type=nullable_string)
parser.add_argument('--dry-run', type=true_or_false)
parser.add_argument('--gitea-instance')
parser.add_argument('--release-repository-name') parser.add_argument('--release-repository-name')
parser.add_argument('--release-ref-name') parser.add_argument('--release-ref-name')
parser.add_argument('--release-run-number') parser.add_argument('--release-run-number')
parser.add_argument('--release-commit-sha') parser.add_argument('--release-commit-sha')
parser.add_argument('--is-pre-release', type=true_or_false) parser.add_argument('--is-pre-release', type=zero_or_one)
parser.add_argument( parser.add_argument(
'--artefact-type', type=nullable_string, '--artefact-type', type=nullable_string,
choices=['oci_image', 'tarball', 'wheel', 'sdist', 'npm']) choices=['oci_image', 'tarball', 'wheel', 'sdist', 'npm'])
parser.add_argument('--artefact-repository', type=nullable_string) parser.add_argument('--artefact-repository', type=nullable_string)
parser.add_argument('--artefact-name', type=nullable_string) parser.add_argument('--artefact-name', type=nullable_string)
parser.add_argument('--artefact-package-name', type=nullable_string)
parser.add_argument('--artefact-filename', type=nullable_string) parser.add_argument('--artefact-filename', type=nullable_string)
parser.add_argument('--artefact-pattern', type=nullable_string) parser.add_argument('--artefact-pattern', type=nullable_string)
parser.add_argument('--artefact-directory', type=nullable_string) parser.add_argument('--artefact-directory', type=nullable_string)
@@ -293,34 +236,18 @@ if __name__ == '__main__':
parser.add_argument('--deployment-repository', type=nullable_string) parser.add_argument('--deployment-repository', type=nullable_string)
parser.add_argument('--deployment-condition', type=nullable_string) parser.add_argument('--deployment-condition', type=nullable_string)
parser.add_argument('--write-env-vars-to-filename')
args = parser.parse_args() args = parser.parse_args()
def clean_repository_name(name: str) -> str:
if name.startswith('//'):
return re.match('^[/][/][^/]+[/](.+)$', name).group(1)
else:
return name
if args.action == 'declare': if args.action == 'declare':
if args.release_yaml_filename is None: project_description = make_project_description(args.version_descriptor)
project_description = make_project_description(
args.version_descriptor)
else:
with open(args.release_yaml_filename, 'r') as f:
project_description = parse_project_description(
yaml.safe_load(f))
project_description = replace( project_description = replace(
project_description, project_description,
context=make_context( context=make_context(args.release_repository_name,
args.gitea_instance, args.release_ref_name,
clean_repository_name(args.release_repository_name), args.release_run_number,
args.release_ref_name, args.release_commit_sha,
args.release_run_number, args.is_pre_release))
args.release_commit_sha,
args.is_pre_release))
save_project_description(args.state, project_description) save_project_description(args.state, project_description)
@@ -331,7 +258,6 @@ if __name__ == '__main__':
args.artefact_repository, args.artefact_repository,
args.artefact_name, args.artefact_name,
args.artefact_filename, args.artefact_filename,
args.artefact_package_name,
args.artefact_pattern, args.artefact_pattern,
args.artefact_directory, args.artefact_directory,
args.version_descriptor) args.version_descriptor)
@@ -358,36 +284,8 @@ if __name__ == '__main__':
save_project_description(args.state, project_description) save_project_description(args.state, project_description)
elif args.action == 'publish-artefacts':
project_description = load_project_description(args.state)
publish_artefacts(project_description, args.dry_run)
elif args.action == 'update-deployments':
project_description = load_project_description(args.state)
update_deployments(project_description, args.dry_run)
elif args.action == 'create-release':
project_description = load_project_description(args.state)
create_release(project_description, args.dry_run)
elif args.action == 'dump': elif args.action == 'dump':
project_description = load_project_description(args.state) dump_project_description(load_project_description(args.state))
dump_project_description(project_description)
elif args.action == 'check':
project_description = load_project_description(args.state)
else: else:
raise NotImplementedError() raise NotImplementedError()
assert project_description is not None
env_var_filename = args.write_env_vars_to_filename
if env_var_filename is not None:
env_vars = project_description.environment_variables
assert not any(map(lambda v: '"' in v, env_vars.values()))
with open(env_var_filename, 'a') as f:
f.write(
'\n'.join(map(lambda it: '%s="%s"' % it, env_vars.items())))

View File

@@ -3,14 +3,8 @@ from dataclasses import dataclass
@dataclass(frozen=True) @dataclass(frozen=True)
class ReleaseContext: class ReleaseContext:
gitea_instance: str
repository_name: str repository_name: str
ref_name: str ref_name: str
run_number: int run_number: int
commit_sha: str commit_sha: str
is_pre_release: bool is_pre_release: bool
def make_environment_variables(self, version):
return {
'RELEASE_IS_PRERELEASE': ('1' if self.is_pre_release else '0'),
}

View File

@@ -1,163 +1,45 @@
import operator
from dataclasses import dataclass, field from dataclasses import dataclass, field
from enum import Enum from enum import Enum
from functools import cached_property, reduce
from itertools import chain
from typing import Optional, Union from typing import Optional, Union
from release import toolkit, versioning from semver import Version
from release.context import ReleaseContext
from release.versioning import Version
DEFAULT_OCI_IMAGE_REPOSITORY = 'europe-docker.pkg.dev/puzzle-and-play/docker' from release.context import ReleaseContext
DEFAULT_HELM_CHART_REPOSITORY = 'europe-docker.pkg.dev/puzzle-and-play/helm'
DEFAULT_OCI_IMAGE_REPOSITORY = 'europe-docker.pkg.dev/puzzle-and-play/helm'
DEFAULT_GITEA_PACKAGE_INSTANCE = 'https://gitea.puzzleyou.net' DEFAULT_GITEA_PACKAGE_INSTANCE = 'https://gitea.puzzleyou.net'
DEFAULT_PYPI_REPOSITORY_NAME = 'gitea' DEFAULT_PYPI_REPOSITORY_NAME = 'gitea'
HELM_DEFAULT_TIMEOUT = '5m'
def _normalize_env_var_fragment(txt: str) -> str:
return txt.upper().replace('-', '_').replace('.', '_')
def _python_version_str(version: Version) -> str:
return ('%d.%d.%d' % (version.major, version.minor, version.patch)) \
+ ('' if version.prerelease is None else '.%s' % version.prerelease)
@dataclass(frozen=True)
class OciImageReleaseInfo:
image_name: str
local_tag: str
local_full_name: str
remote_full_names: str
tags: list[str]
@dataclass(frozen=True) @dataclass(frozen=True)
class OciImage: class OciImage:
# NOTE: tag is exported as IMAGE_TAG_$NAME
name: str name: str
repository: str = DEFAULT_OCI_IMAGE_REPOSITORY repository: str = DEFAULT_OCI_IMAGE_REPOSITORY
def make_environment_variables(self, context, version):
tag = context.commit_sha
normalized_image_name = _normalize_env_var_fragment(self.name)
release_info = self.make_release_info(context, version)
return {
'RELEASE_IMAGE_TAG': tag,
'RELEASE_IMAGE_LOCAL_NAME_%s' % normalized_image_name:
release_info.local_full_name,
}
def make_release_info(self, context: ReleaseContext, version: Version):
version_tag = 'v%s' % version
tags = ([version_tag, '%s.latest' % context.ref_name] + (
['development']
if context.is_pre_release
else [
'v%d.%d' % (version.major, version.minor),
'v%d' % version.major,
'latest',
]))
remote_full_names = list(
map(lambda tag: '%s/%s:%s' % (self.repository, self.name, tag),
tags))
return OciImageReleaseInfo(
image_name=self.name,
local_tag=context.commit_sha,
local_full_name='%s:%s' % (self.name, context.commit_sha),
remote_full_names=remote_full_names,
tags=tags)
@dataclass(frozen=True)
class TarballReleaseInfo:
filename: str
package_name: str
version_str: str
repository: str
@dataclass(frozen=True) @dataclass(frozen=True)
class Tarball: class Tarball:
filename: str filename: str
package_name: str
repository: str = DEFAULT_GITEA_PACKAGE_INSTANCE repository: str = DEFAULT_GITEA_PACKAGE_INSTANCE
def make_environment_variables(self, context, version):
return {}
def make_release_info(self, context: ReleaseContext, version: Version):
return TarballReleaseInfo(
filename=self.filename,
package_name=self.package_name,
repository=self.repository,
version_str=str(version))
@dataclass(frozen=True)
class WheelReleaseInfo:
pattern: str
repository: str
version_str: str
@dataclass(frozen=True) @dataclass(frozen=True)
class Wheel: class Wheel:
pattern: str pattern: str
repository: str = DEFAULT_PYPI_REPOSITORY_NAME repository: str = DEFAULT_PYPI_REPOSITORY_NAME
def make_environment_variables(self, context, version):
return {}
def make_release_info(self, context: ReleaseContext, version: Version):
return WheelReleaseInfo(
pattern=self.pattern,
repository=self.repository,
version_str=_python_version_str(version))
@dataclass(frozen=True)
class SdistReleaseInfo:
filename: str
repository: str
version_str: str
@dataclass(frozen=True) @dataclass(frozen=True)
class Sdist: class Sdist:
filename: str filename: str
repository: str = DEFAULT_PYPI_REPOSITORY_NAME repository: str = DEFAULT_PYPI_REPOSITORY_NAME
def make_environment_variables(self, context, version):
return {}
def make_release_info(self, context: ReleaseContext, version: Version):
return SdistReleaseInfo(
filename=self.filename,
repository=self.repository,
version_str=_python_version_str(version))
@dataclass(frozen=True)
class NpmReleaseInfo:
directory: str
@dataclass(frozen=True) @dataclass(frozen=True)
class Npm: class Npm:
directory: str directory: str
def make_environment_variables(self, context, version):
return {}
def make_release_info(self, context: ReleaseContext, version: Version):
return NpmReleaseInfo(directory=self.directory)
@dataclass(frozen=True) @dataclass(frozen=True)
class ArtefactDescription: class ArtefactDescription:
@@ -165,35 +47,12 @@ class ArtefactDescription:
version_descriptor: Optional[str] = None # if different from main version_descriptor: Optional[str] = None # if different from main
@dataclass(frozen=True)
class HelmReleaseInfo:
release_name: str
namespace: str
repository: str
image_tag: str
image_paths: str
timeout: str
@dataclass(frozen=True) @dataclass(frozen=True)
class HelmRelease: class HelmRelease:
release_name: str release_name: str
namespace: str = None namespace: str
image_paths: list[str] = field(default_factory=lambda: ['image.tag']) image_paths: list[str] = field(default_factory=lambda: ['image.tag'])
repository: str = DEFAULT_HELM_CHART_REPOSITORY repository: str = DEFAULT_OCI_IMAGE_REPOSITORY
def make_environment_variables(self, context, version):
return {}
def make_release_info(self, context: ReleaseContext, version: Version):
return HelmReleaseInfo(
release_name=self.release_name,
namespace=self.namespace or self.release_name,
repository=self.repository,
image_tag='v%s' % version,
image_paths=self.image_paths,
timeout=HELM_DEFAULT_TIMEOUT,
)
class DeploymentCondition(Enum): class DeploymentCondition(Enum):
@@ -209,15 +68,6 @@ class DeploymentDescription:
condition: DeploymentCondition = DeploymentCondition.PRE_RELEASE_ONLY condition: DeploymentCondition = DeploymentCondition.PRE_RELEASE_ONLY
@dataclass(frozen=True)
class ProjectReleaseInfo:
gitea_release_title: str
gitea_release_description: str
gitea_is_prerelease: bool
gitea_git_commitish: str
git_tags: list[str]
@dataclass(frozen=True) @dataclass(frozen=True)
class ProjectDescription: class ProjectDescription:
version_descriptor: str # filename version_descriptor: str # filename
@@ -225,182 +75,4 @@ class ProjectDescription:
deployments: list[DeploymentDescription] = field( deployments: list[DeploymentDescription] = field(
default_factory=lambda: []) default_factory=lambda: [])
context: Optional[ReleaseContext] = None context: Optional[ReleaseContext] = None
planned_version: Optional[Version] = None
@cached_property
def release_info(self):
if self.context is None:
return None
version = self.planned_version
return ProjectReleaseInfo(
gitea_release_title='Version %s' % version,
gitea_release_description='',
gitea_is_prerelease=self.context.is_pre_release,
gitea_git_commitish=self.context.commit_sha,
git_tags=['v%s' % version] + (
['development']
if self.context.is_pre_release
else [
'v%d.%d' % (version.major, version.minor),
'v%d' % version.major,
'latest',
]))
@cached_property
def planned_version(self):
if self.context is not None and self.context.is_pre_release:
prerelease_version = self.context.run_number or 0
return self.project_version.replace(
prerelease='dev%s' % prerelease_version)
else:
return self.project_version
@cached_property
def project_version(self):
return versioning.use_any(self.version_descriptor).version
@cached_property
def is_released(self):
if self.gitea_tool is None:
return None
return self.gitea_tool.is_released(self.planned_version)
@cached_property
def gitea_tool(self):
if self.context is None:
return None
if self.context.repository_name is None:
return None
if self.context.gitea_instance is None:
return None
return toolkit.Gitea(self.context.gitea_instance,
self.context.repository_name,
self.context.is_pre_release or False)
@property
def environment_variables(self):
version = self.planned_version
return reduce(
operator.or_,
chain(
map(lambda a:
a.generated.make_environment_variables(
self.context, version),
self.artefacts),
map(lambda d:
d.deployment.make_environment_variables(
self.context, version),
self.deployments),
([self.context.make_environment_variables(version)]
if self.context is not None
else []),
[{
'RELEASE_PROJECT_CURRENT_VERSION':
str(self.project_version),
'RELEASE_PROJECT_PLANNED_VERSION':
str(self.planned_version),
}]
))
def parse_project_description(obj):
def optional(obj, field, acc=lambda o, f: o[f]):
if field in obj:
return {field: acc(obj, field)}
else:
return {}
def parse_oci_image(img):
assert 'name' in img
return OciImage(name=img['name'],
**optional(img, 'repository'))
def parse_tarball(tar):
assert 'filename' in tar
return Tarball(filename=tar['filename'],
package_name=tar['package_name'],
**optional(tar, 'repository'))
def parse_wheel(whl):
assert 'pattern' in whl
return Wheel(pattern=whl['pattern'],
**optional(whl, 'repository'))
def parse_sdist(sdist):
assert 'filename' in sdist
return Sdist(filename=sdist['filename'],
**optional(sdist, 'repository'))
def parse_npm(npm):
assert 'directory' in npm
return Npm(directory=npm['directory'])
def parse_artefact(art):
assert 'type' in art
if art['type'] == 'oci_image' or art['type'] == 'docker':
generated = parse_oci_image(art)
elif art['type'] == 'tarball':
generated = parse_tarball(art)
elif art['type'] == 'wheel':
generated = parse_wheel(art)
elif art['type'] == 'sdist':
generated = parse_sdist(art)
elif art['type'] == 'npm':
generated = parse_npm(art)
else:
raise Exception('unknown artefact type: %s' % art['type'])
return ArtefactDescription(
generated=generated,
**optional(art, 'version_descriptor',))
def parse_helm(helm):
assert 'release_name' in helm
return HelmRelease(
release_name=helm['release_name'],
**optional(helm, 'namespace'),
**optional(helm, 'image_paths'),
**optional(helm, 'repository'))
def parse_condition(cnd, _):
assert 'condition' in cnd
txt = cnd['condition'].lower()
if txt == 'always':
return DeploymentCondition.ALWAYS
if txt == 'never':
return DeploymentCondition.NEVER
if txt == 'pre_release_only':
return DeploymentCondition.PRE_RELEASE_ONLY
if txt == 'release_only':
return DeploymentCondition.RELEASE_ONLY
else:
raise Exception('unknown condition: %s' % txt)
def parse_deployment(dpl):
assert 'type' in dpl
if dpl['type'] == 'helm':
deployment = parse_helm(dpl)
else:
raise Exception('unknown deployment type: %s' % dpl['type'])
return DeploymentDescription(
deployment=deployment,
**optional(dpl, 'condition', parse_condition))
assert 'version_descriptor' in obj
return ProjectDescription(
version_descriptor=obj['version_descriptor'],
artefacts=list(map(parse_artefact, obj.get('artefacts', []))),
deployments=list(map(parse_deployment, obj.get('deployments', []))),
)

View File

@@ -1,233 +0,0 @@
import json
import subprocess
from os import path
from tempfile import NamedTemporaryFile, TemporaryDirectory
from release.project import (DeploymentCondition, HelmRelease, HelmReleaseInfo,
Npm, NpmReleaseInfo, OciImage,
OciImageReleaseInfo, ProjectDescription, Sdist,
SdistReleaseInfo, Tarball, TarballReleaseInfo,
Wheel, WheelReleaseInfo)
class Cli:
def __init__(self, dry_run: bool):
self.dry_run = dry_run
def __call__(self, *cmd, cwd=None):
try:
cleaned_cmd = list(filter(lambda it: it != '', cmd))
if cwd is not None:
print('$> cd %s' % cwd)
print('$> %s' % ' '.join(cleaned_cmd))
if not self.dry_run:
res = subprocess.run(cleaned_cmd,
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
cwd=cwd)
output = res.stdout
else:
output = b''
for line in output.decode('utf-8').splitlines():
print(' | %s' % line)
return output
except subprocess.CalledProcessError as exc:
for line in exc.stdout.decode('utf-8').splitlines():
print('[ERR] %s' % line)
raise Exception('%s failed: %s' % (cleaned_cmd[0],
exc.stderr.decode('utf-8')))
def publish_oci_image(info: OciImageReleaseInfo, cli: Cli):
print('-- publishing oci image --')
print('name: %s' % info.image_name)
for remote_full_name in info.remote_full_names:
cli('docker', 'tag', info.local_full_name, remote_full_name)
cli('docker', 'push', remote_full_name)
print()
def publish_tarball(info: TarballReleaseInfo, cli: Cli):
print('-- publishing tarball --')
print('filename: %s' % info.filename)
cli('curl',
'--netrc',
'--fail-with-body',
'--upload-file', info.filename,
'-i',
'-X', 'PUT',
'%s/api/packages/puzzleYOU/generic/%s/%s/%s' % ( # TODO owner
info.repository,
info.package_name,
info.version_str,
path.basename(info.filename)))
print()
def publish_wheel(info: WheelReleaseInfo, cli: Cli):
print('-- publishing wheel --')
cli('twine',
'upload',
'--verbose',
'--repository', info.repository,
info.pattern)
print()
def publish_sdist(info: SdistReleaseInfo, cli: Cli):
print('-- publishing python source distribution --')
cli('twine',
'upload',
'--verbose',
'--repository', info.repository,
info.filename)
print()
def publish_npm(info: NpmReleaseInfo, cli: Cli):
print('-- publishing npm package --')
cli('npm', 'publish', cwd=info.directory)
print()
def publish_artefacts(project: ProjectDescription, dry_run: bool = False):
cli = Cli(dry_run)
context = project.context
planned_version = project.planned_version
for artefact in project.artefacts:
release_info = artefact.generated.make_release_info(
context, planned_version)
if isinstance(artefact.generated, OciImage):
publish_oci_image(release_info, cli)
elif isinstance(artefact.generated, Tarball):
publish_tarball(release_info, cli)
elif isinstance(artefact.generated, Wheel):
publish_wheel(release_info, cli)
elif isinstance(artefact.generated, Sdist):
publish_sdist(release_info, cli)
elif isinstance(artefact.generated, Npm):
publish_npm(release_info, cli)
else:
print('unknown artefact: %s' % artefact.generated)
raise Exception('cannot publish unknown artefact')
def update_helm_release(info: HelmReleaseInfo, cli: Cli):
ro_cli = Cli(dry_run=False)
print('-- updating helm release --')
metadata = json.loads(ro_cli('helm',
'get', 'metadata', info.release_name,
'-n', info.namespace, '-o', 'json').decode())
chart_name = metadata['chart']
chart_version = metadata['version']
with TemporaryDirectory() as td:
ro_cli('helm',
'pull', 'oci://%s/%s' % (info.repository, chart_name),
'--version', chart_version,
'-d', td)
chart_filename = '%s/%s-%s.tgz' % (td, chart_name, chart_version)
value_overrides = ','.join(
map(lambda t: '%s=%s' % (t, info.image_tag), info.image_paths))
cli('helm',
'upgrade', info.release_name,
chart_filename,
'--version', chart_version,
'--namespace', info.namespace,
'--reuse-values',
'--set', value_overrides,
'--timeout', info.timeout)
def update_deployments(project: ProjectDescription, dry_run: bool = False):
cli = Cli(dry_run)
context = project.context
planned_version = project.planned_version
for deployment in project.deployments:
release_info = deployment.deployment.make_release_info(
context, planned_version)
if deployment.condition == DeploymentCondition.NEVER:
continue
if (deployment.condition == DeploymentCondition.RELEASE_ONLY
and context.is_pre_release):
continue
if (deployment.condition == DeploymentCondition.PRE_RELEASE_ONLY
and not context.is_pre_release):
continue
if isinstance(deployment.deployment, HelmRelease):
update_helm_release(release_info, cli)
else:
print('unknown deployment: %s' % deployment.deployment)
raise Exception('cannot publish unknown deployment')
def create_release(project: ProjectDescription, dry_run: bool = False):
cli = Cli(dry_run)
info = project.release_info
context = project.context
assert len(info.git_tags) > 0
# https://gitea.com/api/swagger#/repository/repoCreateRelease
payload = {
'body': info.gitea_release_description,
'draft': False,
'name': info.gitea_release_title,
'prerelease': info.gitea_is_prerelease,
'tag_message': info.gitea_release_title,
'tag_name': info.git_tags[0],
'target_commitish': info.gitea_git_commitish,
}
print('-- creating gitea release --')
print('POST body:')
print(json.dumps(payload, indent=2))
with NamedTemporaryFile(mode='w') as tf:
json.dump(payload, tf)
tf.flush()
cli('curl',
'--netrc',
'--fail-with-body',
'-i',
'-H', 'Content-Type: application/json',
'--data', '@%s' % tf.name,
'-X', 'POST',
'%s/api/v1/repos/%s/releases' % (context.gitea_instance,
context.repository_name))
print()
print('-- adding git tags --')
for tag in info.git_tags[1:]:
cli('git', 'tag', '-f', tag, info.gitea_git_commitish)
cli('git', 'push', '-f', 'origin', '--tags')

View File

@@ -1,50 +0,0 @@
version_descriptor: 'test-assets/version.txt'
artefacts:
- type: oci_image
name: productdesignerd
- type: docker
name: motacilla
version_descriptor: 'test-assets/Cargo.toml'
repository: remote
- type: tarball
package_name: 'yaac'
filename: '/tmp/yaac.tar.gz'
- type: tarball
package_name: 'yaac'
filename: '/tmp/yaac.tar.gz'
repository: balls
- type: wheel
pattern: './scratch/wheels/*.whl'
- type: wheel
pattern: './scratch/wheels/*.whl'
repository: other
- type: sdist
filename: './dist/papyru-0.0.1.tar.gz'
- type: sdist
filename: './dist/papyru-0.0.1.tar.gz'
repository: 'pypi'
- type: npm
directory: ./dist/browser
deployments:
- type: helm
release_name: 'productdesignerd-testing'
- type: helm
condition: release_only
release_name: foo
namespace: bar
image_paths:
- foo
- bar
- baz
repository: helms

View File

@@ -9,5 +9,4 @@ class TestReleaseContext(TestCase):
ref_name='testing', ref_name='testing',
run_number=42, run_number=42,
is_pre_release=True, is_pre_release=True,
commit_sha='0AB123', commit_sha='0AB123')
gitea_instance='http://gitea.foo.intern')

View File

@@ -1,17 +1,8 @@
from os import path
from unittest import TestCase from unittest import TestCase
import yaml
from semver import Version
from release.context import ReleaseContext
from release.project import (ArtefactDescription, DeploymentCondition, from release.project import (ArtefactDescription, DeploymentCondition,
DeploymentDescription, HelmRelease, DeploymentDescription, HelmRelease, Npm, OciImage,
HelmReleaseInfo, Npm, NpmReleaseInfo, OciImage, ProjectDescription, Sdist, Tarball, Wheel)
OciImageReleaseInfo, ProjectDescription,
ProjectReleaseInfo, Sdist, SdistReleaseInfo,
Tarball, TarballReleaseInfo, Wheel,
WheelReleaseInfo, parse_project_description)
class TestProjectDescription(TestCase): class TestProjectDescription(TestCase):
@@ -60,9 +51,7 @@ class TestProjectDescription(TestCase):
ProjectDescription( ProjectDescription(
version_descriptor='version.txt', version_descriptor='version.txt',
artefacts=[ artefacts=[
ArtefactDescription(generated=Tarball( ArtefactDescription(generated=Tarball('/tmp/yaac.tar.gz'))
filename='/tmp/yaac.tar.gz',
package_name='yaac'))
]) ])
# papyru # papyru
@@ -135,255 +124,3 @@ class TestProjectDescription(TestCase):
release_name='prngl-testing', release_name='prngl-testing',
namespace='prngl-testing')) namespace='prngl-testing'))
]) ])
def test_environment_variables(self):
# motacilla
desc = ProjectDescription(
version_descriptor=path.join(
path.dirname(__file__), 'assets/setup.py'),
artefacts=[
ArtefactDescription(generated=OciImage(
repository='remote', name='motacilla')),
ArtefactDescription(generated=OciImage(
repository='remote', name='motacilla-cdn')),
],
deployments=[
DeploymentDescription(
condition=DeploymentCondition.PRE_RELEASE_ONLY,
deployment=HelmRelease(
release_name='motacilla-de-testing',
namespace='motacilla-de-testing',
image_paths=['image.cms.tag', 'image.cdn.tag'])),
DeploymentDescription(
condition=DeploymentCondition.PRE_RELEASE_ONLY,
deployment=HelmRelease(
release_name='motacilla-schmidt-testing',
namespace='motacilla-schmidt-testing',
image_paths=['image.cms.tag', 'image.cdn.tag'])),
DeploymentDescription(
condition=DeploymentCondition.PRE_RELEASE_ONLY,
deployment=HelmRelease(
release_name='motacilla-be-testing',
namespace='motacilla-be-testing',
image_paths=['image.cms.tag', 'image.cdn.tag'])),
],
context=ReleaseContext(repository_name='resi',
ref_name='testing',
run_number=42,
is_pre_release=True,
commit_sha='0AB123',
gitea_instance='http://gitea.foo.intern')
)
self.maxDiff = None
self.assertEqual(
desc.environment_variables,
{
'RELEASE_IMAGE_TAG': '0AB123',
'RELEASE_IMAGE_LOCAL_NAME_MOTACILLA': 'motacilla:0AB123',
'RELEASE_IMAGE_LOCAL_NAME_MOTACILLA_CDN':
'motacilla-cdn:0AB123',
'RELEASE_IS_PRERELEASE': '1',
'RELEASE_PROJECT_CURRENT_VERSION': '2.10.4',
'RELEASE_PROJECT_PLANNED_VERSION': '2.10.4-dev42'
}
)
def test_tarball_release_info(self):
tarball = Tarball(filename='foo.tar.gz', package_name='foo')
self.assertEqual(
TarballReleaseInfo(
filename='foo.tar.gz',
package_name='foo',
version_str='1.2.3-dev4',
repository='https://gitea.puzzleyou.net'),
tarball.make_release_info(None, Version(1, 2, 3, 'dev4')))
def test_wheel_release_info(self):
wheel = Wheel(pattern='dist/wheels/*')
self.assertEqual(
WheelReleaseInfo(pattern='dist/wheels/*',
repository='gitea',
version_str='1.2.3.dev4'),
wheel.make_release_info(None, Version(1, 2, 3, 'dev4')))
def test_sdist_release_info(self):
sdist = Sdist(filename='somethingsomething.tar.gz')
self.assertEqual(
SdistReleaseInfo(version_str='1.2.3.dev4',
filename='somethingsomething.tar.gz',
repository='gitea'),
sdist.make_release_info(None, Version(1, 2, 3, 'dev4')))
def test_npm_release_info(self):
npm = Npm(directory='./')
self.assertEqual(
NpmReleaseInfo(directory='./'),
npm.make_release_info(None, Version(1, 2, 3, 'dev4')))
def test_helm_release_info(self):
helm = HelmRelease(release_name='foo',
namespace='bar',
image_paths=['foo.bar', 'baz.blubb'],
repository='ludicrous')
self.assertEqual(
HelmReleaseInfo(release_name='foo',
namespace='bar',
repository='ludicrous',
image_tag='v1.2.3-dev4',
image_paths=['foo.bar', 'baz.blubb'],
timeout='5m'),
helm.make_release_info(None, Version(1, 2, 3, 'dev4')))
def test_oci_image_release_info(self):
self.maxDiff = None
img = OciImage(name='test', repository='remote')
pre_release_version = Version(1, 33, 7, 'dev42')
pre_release_context = ReleaseContext(
repository_name='resi',
ref_name='testing',
run_number=42,
is_pre_release=True,
commit_sha='PROBABLY_BROKEN',
gitea_instance='http://gitea.foo.intern')
self.assertEqual(
OciImageReleaseInfo(
image_name='test',
local_tag='PROBABLY_BROKEN',
local_full_name='test:PROBABLY_BROKEN',
remote_full_names=[
'remote/test:v1.33.7-dev42',
'remote/test:testing.latest',
'remote/test:development',
],
tags=['v1.33.7-dev42', 'testing.latest', 'development']),
img.make_release_info(pre_release_context,
pre_release_version))
release_version = Version(1, 33, 7)
release_context = ReleaseContext(
repository_name='resi',
ref_name='master',
run_number=43,
is_pre_release=False,
commit_sha='finalv2',
gitea_instance='http://gitea.foo.intern')
self.assertEqual(
OciImageReleaseInfo(
image_name='test',
local_tag='finalv2',
local_full_name='test:finalv2',
remote_full_names=[
'remote/test:v1.33.7',
'remote/test:master.latest',
'remote/test:v1.33',
'remote/test:v1',
'remote/test:latest',
],
tags=['v1.33.7',
'master.latest',
'v1.33',
'v1',
'latest']),
img.make_release_info(release_context, release_version))
def test_project_release_info(self):
project_0 = ProjectDescription(version_descriptor='version.txt')
self.assertIsNone(project_0.release_info)
project_pre = ProjectDescription(
version_descriptor='version.txt',
context=ReleaseContext(
repository_name='resi',
ref_name='testing',
run_number=42,
is_pre_release=True,
commit_sha='PROBABLY_BROKEN',
gitea_instance='http://gitea.foo.intern'))
self.assertEqual(
ProjectReleaseInfo(
gitea_release_title='Version 0.0.1-dev42',
gitea_release_description='',
gitea_is_prerelease=True,
gitea_git_commitish='PROBABLY_BROKEN',
git_tags=['v0.0.1-dev42', 'development'],
),
project_pre.release_info)
project = ProjectDescription(
version_descriptor='version.txt',
context=ReleaseContext(
repository_name='resi',
ref_name='master',
run_number=42,
is_pre_release=False,
commit_sha='PROBABLY_BROKEN',
gitea_instance='http://gitea.foo.intern'))
self.assertEqual(
ProjectReleaseInfo(
gitea_release_title='Version 0.0.1',
gitea_release_description='',
gitea_is_prerelease=False,
gitea_git_commitish='PROBABLY_BROKEN',
git_tags=['v0.0.1', 'v0.0', 'v0', 'latest'],
),
project.release_info)
def test_load_from_file(self):
expected = ProjectDescription(
version_descriptor='test-assets/version.txt',
artefacts=[
ArtefactDescription(generated=OciImage(
name='productdesignerd')),
ArtefactDescription(
generated=OciImage(repository='remote', name='motacilla'),
version_descriptor='test-assets/Cargo.toml'),
ArtefactDescription(generated=Tarball(
filename='/tmp/yaac.tar.gz', package_name='yaac')),
ArtefactDescription(generated=Tarball(
filename='/tmp/yaac.tar.gz',
package_name='yaac',
repository='balls')),
ArtefactDescription(generated=Wheel(
pattern='./scratch/wheels/*.whl')),
ArtefactDescription(generated=Wheel(
pattern='./scratch/wheels/*.whl', repository='other')),
ArtefactDescription(generated=Sdist(
filename='./dist/papyru-0.0.1.tar.gz')),
ArtefactDescription(generated=Sdist(
filename='./dist/papyru-0.0.1.tar.gz',
repository='pypi')),
ArtefactDescription(generated=Npm(directory='./dist/browser')),
],
deployments=[
DeploymentDescription(
condition=DeploymentCondition.PRE_RELEASE_ONLY,
deployment=HelmRelease(
release_name='productdesignerd-testing')),
DeploymentDescription(
condition=DeploymentCondition.RELEASE_ONLY,
deployment=HelmRelease(
release_name='foo',
namespace='bar',
image_paths=['foo', 'bar', 'baz'],
repository='helms',
)),
])
with open(path.join(
path.dirname(__file__), 'assets/release.yaml'), 'r') as f:
project = parse_project_description(yaml.safe_load(f))
self.maxDiff = None
self.assertEqual(project, expected)

View File

@@ -1,36 +0,0 @@
import requests
class Gitea:
def __init__(self,
gitea_instance: str,
repository_name: str,
is_pre_release: bool):
self.instance = gitea_instance
self.repository_name = repository_name
self.is_pre_release = is_pre_release
def is_released(self, version):
resp = requests.get(
'%s/repos/%s/releases/tags/%s' % (self.api_url,
self.repository_name,
version_to_git_tag(version)))
if resp.status_code == 404:
return False
resp.raise_for_status()
return True
def api_get(self, endpoint):
resp = requests.get('%s/%s' % (self.api_url, endpoint))
resp.raise_for_status()
return resp.json()
@property
def api_url(self):
return '%s/api/v1' % self.instance
def version_to_git_tag(version):
return 'v%s' % version