Merge branch 'main' into kotlin-compile
This commit is contained in:
commit
b3a612c938
34 changed files with 577 additions and 198 deletions
42
.github/workflows/stable.yml
vendored
42
.github/workflows/stable.yml
vendored
|
@ -1,42 +0,0 @@
|
||||||
name: stable
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- '[0-9]+.[0-9]+.[0-9]+'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
name: stable build
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Use python 3.x
|
|
||||||
uses: actions/setup-python@v2
|
|
||||||
with:
|
|
||||||
python-version: '3.x'
|
|
||||||
|
|
||||||
- name: install dependencies
|
|
||||||
run: |
|
|
||||||
python -m pip install --upgrade pip
|
|
||||||
pip install -r requirements.txt
|
|
||||||
|
|
||||||
- name: build stable release
|
|
||||||
env:
|
|
||||||
TWINE_USERNAME: __token__
|
|
||||||
TWINE_PASSWORD: ${{ secrets.PYPI_DDA }}
|
|
||||||
run: |
|
|
||||||
pyb -P version=${{ github.ref }} publish upload
|
|
||||||
|
|
||||||
- name: Create GH Release
|
|
||||||
id: create_release
|
|
||||||
uses: actions/create-release@v1
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
with:
|
|
||||||
tag_name: ${{ github.ref }}
|
|
||||||
release_name: Release ${{ github.ref }}
|
|
||||||
draft: false
|
|
||||||
prerelease: false
|
|
||||||
|
|
30
.github/workflows/unstable.yml
vendored
30
.github/workflows/unstable.yml
vendored
|
@ -1,30 +0,0 @@
|
||||||
name: unstable
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- '![0-9]+.[0-9]+.[0-9]+'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
name: unstable
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Use python 3.x
|
|
||||||
uses: actions/setup-python@v2
|
|
||||||
with:
|
|
||||||
python-version: '3.x'
|
|
||||||
|
|
||||||
- name: install dependencies
|
|
||||||
run: |
|
|
||||||
python -m pip install --upgrade pip
|
|
||||||
pip install -r requirements.txt
|
|
||||||
|
|
||||||
- name: build unstable release
|
|
||||||
env:
|
|
||||||
TWINE_USERNAME: __token__
|
|
||||||
TWINE_PASSWORD: ${{ secrets.PYPI_DDA }}
|
|
||||||
run: |
|
|
||||||
pyb publish upload
|
|
|
@ -4,13 +4,14 @@ stages:
|
||||||
- image
|
- image
|
||||||
|
|
||||||
.py: &py
|
.py: &py
|
||||||
image: "domaindrivenarchitecture/ddadevops-python:4.1.0"
|
image: "domaindrivenarchitecture/ddadevops-python:4.7.0"
|
||||||
before_script:
|
before_script:
|
||||||
|
- export RELEASE_ARTIFACT_TOKEN=$MEISSA_REPO_BUERO_RW
|
||||||
- python --version
|
- python --version
|
||||||
- pip install -r requirements.txt
|
- pip install -r requirements.txt
|
||||||
|
|
||||||
.img: &img
|
.img: &img
|
||||||
image: "domaindrivenarchitecture/ddadevops-dind:4.1.0"
|
image: "domaindrivenarchitecture/ddadevops-dind:4.7.0"
|
||||||
services:
|
services:
|
||||||
- docker:dind
|
- docker:dind
|
||||||
before_script:
|
before_script:
|
||||||
|
@ -43,7 +44,7 @@ pypi-stable:
|
||||||
<<: *tag_only
|
<<: *tag_only
|
||||||
stage: upload
|
stage: upload
|
||||||
script:
|
script:
|
||||||
- pyb -P version=$CI_COMMIT_TAG publish upload
|
- pyb -P version=$CI_COMMIT_TAG publish upload publish_artifacts
|
||||||
|
|
||||||
clj-cljs-image-publish:
|
clj-cljs-image-publish:
|
||||||
<<: *img
|
<<: *img
|
||||||
|
@ -59,7 +60,6 @@ clj-image-publish:
|
||||||
script:
|
script:
|
||||||
- cd infrastructure/clj && pyb image publish
|
- cd infrastructure/clj && pyb image publish
|
||||||
|
|
||||||
|
|
||||||
python-image-publish:
|
python-image-publish:
|
||||||
<<: *img
|
<<: *img
|
||||||
<<: *tag_only
|
<<: *tag_only
|
||||||
|
|
|
@ -205,8 +205,3 @@ For more details about our repository model see: https://repo.prod.meissa.de/mei
|
||||||
|
|
||||||
Copyright © 2021 meissa GmbH
|
Copyright © 2021 meissa GmbH
|
||||||
Licensed under the [Apache License, Version 2.0](LICENSE) (the "License")
|
Licensed under the [Apache License, Version 2.0](LICENSE) (the "License")
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
Copyright © 2023 meissa GmbH
|
|
||||||
Licensed under the [Apache License, Version 2.0](LICENSE) (the "License")
|
|
||||||
|
|
14
build.py
14
build.py
|
@ -33,7 +33,7 @@ default_task = "dev"
|
||||||
name = "ddadevops"
|
name = "ddadevops"
|
||||||
MODULE = "not-used"
|
MODULE = "not-used"
|
||||||
PROJECT_ROOT_PATH = "."
|
PROJECT_ROOT_PATH = "."
|
||||||
version = "4.3.2-dev"
|
version = "4.7.5-dev"
|
||||||
summary = "tools to support builds combining gopass, terraform, dda-pallet, aws & hetzner-cloud"
|
summary = "tools to support builds combining gopass, terraform, dda-pallet, aws & hetzner-cloud"
|
||||||
description = __doc__
|
description = __doc__
|
||||||
authors = [Author("meissa GmbH", "buero@meissa-gmbh.de")]
|
authors = [Author("meissa GmbH", "buero@meissa-gmbh.de")]
|
||||||
|
@ -46,7 +46,7 @@ license = "Apache Software License"
|
||||||
def initialize(project):
|
def initialize(project):
|
||||||
# project.build_depends_on('mockito')
|
# project.build_depends_on('mockito')
|
||||||
# project.build_depends_on('unittest-xml-reporting')
|
# project.build_depends_on('unittest-xml-reporting')
|
||||||
project.build_depends_on("ddadevops>=4.0.0")
|
project.build_depends_on("ddadevops>=4.7.0")
|
||||||
|
|
||||||
project.set_property("verbose", True)
|
project.set_property("verbose", True)
|
||||||
project.get_property("filter_resources_glob").append(
|
project.get_property("filter_resources_glob").append(
|
||||||
|
@ -104,6 +104,10 @@ def initialize(project):
|
||||||
"infrastructure/clj/build.py",
|
"infrastructure/clj/build.py",
|
||||||
"infrastructure/kotlin/build.py",
|
"infrastructure/kotlin/build.py",
|
||||||
],
|
],
|
||||||
|
"release_artifacts": [],
|
||||||
|
"release_artifact_server_url": "https://repo.prod.meissa.de",
|
||||||
|
"release_organisation": "meissa",
|
||||||
|
"release_repository_name": "dda-devops-build",
|
||||||
}
|
}
|
||||||
|
|
||||||
build = ReleaseMixin(project, input)
|
build = ReleaseMixin(project, input)
|
||||||
|
@ -179,6 +183,12 @@ def tag(project):
|
||||||
build.tag_bump_and_push_release()
|
build.tag_bump_and_push_release()
|
||||||
|
|
||||||
|
|
||||||
|
@task
|
||||||
|
def publish_artifacts(project):
|
||||||
|
build = get_devops_build(project)
|
||||||
|
build.publish_artifacts()
|
||||||
|
|
||||||
|
|
||||||
def release(project):
|
def release(project):
|
||||||
prepare(project)
|
prepare(project)
|
||||||
tag(project)
|
tag(project)
|
||||||
|
|
|
@ -23,12 +23,9 @@ classDiagram
|
||||||
| build_dir_name | name of dir, build is executed in | target |
|
| build_dir_name | name of dir, build is executed in | target |
|
||||||
| build_types | list of special builds used. Valid values are ["IMAGE", "C4K", "K3S", "TERRAFORM"] | [] |
|
| build_types | list of special builds used. Valid values are ["IMAGE", "C4K", "K3S", "TERRAFORM"] | [] |
|
||||||
| mixin_types | mixins are orthoganl to builds and represent additional capabilities. Valid Values are ["RELEASE"] | [] |
|
| mixin_types | mixins are orthoganl to builds and represent additional capabilities. Valid Values are ["RELEASE"] | [] |
|
||||||
| module | module name - may result in a hierarchy like name/module | |
|
|
||||||
| name | dedicated name of the build | module |
|
|
||||||
| project_root_path | relative path to projects root. Is used to locate the target dir | |
|
|
||||||
| stage | sth. like test, int, acc or prod | |
|
|
||||||
|
|
||||||
## Example Usage
|
## Example Usage
|
||||||
|
|
||||||
### build.py
|
### build.py
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
|
|
@ -30,7 +30,7 @@ classDiagram
|
||||||
| image_dockerhub_user | user to access docker-hub | IMAGE_DOCKERHUB_USER from env or credentials from gopass |
|
| image_dockerhub_user | user to access docker-hub | IMAGE_DOCKERHUB_USER from env or credentials from gopass |
|
||||||
| image_dockerhub_password | password to access docker-hub | IMAGE_DOCKERHUB_PASSWORD from env or credentials from gopass |
|
| image_dockerhub_password | password to access docker-hub | IMAGE_DOCKERHUB_PASSWORD from env or credentials from gopass |
|
||||||
| image_tag | tag for publishing the image | IMAGE_TAG from env |
|
| image_tag | tag for publishing the image | IMAGE_TAG from env |
|
||||||
|
| image_naming | Strategy for calculate the image name. Posible values are [NAME_ONLY,NAME_AND_MODULE] |NAME_ONLY |
|
||||||
|
|
||||||
### Credentials Mapping defaults
|
### Credentials Mapping defaults
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,15 @@
|
||||||
# ReleaseMixin
|
# ReleaseMixin
|
||||||
|
|
||||||
|
- [ReleaseMixin](#releasemixin)
|
||||||
|
- [Input](#input)
|
||||||
|
- [Example Usage just for creating releases](#example-usage-just-for-creating-releases)
|
||||||
|
- [build.py](#buildpy)
|
||||||
|
- [call the build for creating a major release](#call-the-build-for-creating-a-major-release)
|
||||||
|
- [Example Usage for creating a release on forgejo / gitea \& upload the generated artifacts](#example-usage-for-creating-a-release-on-forgejo--gitea--upload-the-generated-artifacts)
|
||||||
|
- [build.py](#buildpy-1)
|
||||||
|
- [call the build](#call-the-build)
|
||||||
|
|
||||||
|
|
||||||
Support for releases following the trunk-based-release flow (see https://trunkbaseddevelopment.com/)
|
Support for releases following the trunk-based-release flow (see https://trunkbaseddevelopment.com/)
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
|
@ -8,6 +18,7 @@ classDiagram
|
||||||
prepare_release() - adjust all build files to carry the correct version & commit locally
|
prepare_release() - adjust all build files to carry the correct version & commit locally
|
||||||
tag_and_push_release() - tag the git repo and push changes to origin
|
tag_and_push_release() - tag the git repo and push changes to origin
|
||||||
update_release_type (release_type) - change the release type during run time
|
update_release_type (release_type) - change the release type during run time
|
||||||
|
publish_artifacts() - publish release & artifacts to forgejo/gitea
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -20,8 +31,12 @@ classDiagram
|
||||||
| release_main_branch | the name of your trank | "main" |
|
| release_main_branch | the name of your trank | "main" |
|
||||||
| release_primary_build_file | path to the build file having the leading version info (read & write). Valid extensions are .clj, .json, .gradle, .py | "./project.clj" |
|
| release_primary_build_file | path to the build file having the leading version info (read & write). Valid extensions are .clj, .json, .gradle, .py | "./project.clj" |
|
||||||
| release_secondary_build_files | list of secondary build files, version is written in. | [] |
|
| release_secondary_build_files | list of secondary build files, version is written in. | [] |
|
||||||
|
| release_artifact_server_url | Optional: The base url of your forgejo/gitea instance to publish a release tode | |
|
||||||
|
| release_organisation | Optional: The repository organisation name | |
|
||||||
|
| release_repository_name | Optional: The repository name name | |
|
||||||
|
| release_artifacts | Optional: The list of artifacts to publish to the release generated name | [] |
|
||||||
|
|
||||||
## Example Usage
|
## Example Usage just for creating releases
|
||||||
|
|
||||||
### build.py
|
### build.py
|
||||||
|
|
||||||
|
@ -36,7 +51,7 @@ PROJECT_ROOT_PATH = '..'
|
||||||
|
|
||||||
@init
|
@init
|
||||||
def initialize(project):
|
def initialize(project):
|
||||||
project.build_depends_on("ddadevops>=4.0.0")
|
project.build_depends_on("ddadevops>=4.7.0")
|
||||||
|
|
||||||
input = {
|
input = {
|
||||||
"name": name,
|
"name": name,
|
||||||
|
@ -49,34 +64,107 @@ def initialize(project):
|
||||||
"release_primary_build_file": "project.clj",
|
"release_primary_build_file": "project.clj",
|
||||||
"release_secondary_build_files": ["package.json"],
|
"release_secondary_build_files": ["package.json"],
|
||||||
}
|
}
|
||||||
|
|
||||||
roject.build_depends_on("ddadevops>=4.0.0-dev")
|
|
||||||
|
|
||||||
build = ReleaseMixin(project, input)
|
build = ReleaseMixin(project, input)
|
||||||
build.initialize_build_dir()
|
build.initialize_build_dir()
|
||||||
|
|
||||||
|
|
||||||
@task
|
@task
|
||||||
def prepare_release(project):
|
def patch(project):
|
||||||
|
linttest(project, "PATCH")
|
||||||
|
release(project)
|
||||||
|
|
||||||
|
|
||||||
|
@task
|
||||||
|
def minor(project):
|
||||||
|
linttest(project, "MINOR")
|
||||||
|
release(project)
|
||||||
|
|
||||||
|
|
||||||
|
@task
|
||||||
|
def major(project):
|
||||||
|
linttest(project, "MAJOR")
|
||||||
|
release(project)
|
||||||
|
|
||||||
|
|
||||||
|
@task
|
||||||
|
def dev(project):
|
||||||
|
linttest(project, "NONE")
|
||||||
|
|
||||||
|
|
||||||
|
@task
|
||||||
|
def prepare(project):
|
||||||
build = get_devops_build(project)
|
build = get_devops_build(project)
|
||||||
build.prepare_release()
|
build.prepare_release()
|
||||||
|
|
||||||
@task
|
|
||||||
def build(project):
|
|
||||||
print("do the build")
|
|
||||||
|
|
||||||
@task
|
@task
|
||||||
def publish(project):
|
def tag(project):
|
||||||
print("publish your artefacts")
|
|
||||||
|
|
||||||
@task
|
|
||||||
def after_publish(project):
|
|
||||||
build = get_devops_build(project)
|
build = get_devops_build(project)
|
||||||
build.tag_bump_and_push_release()
|
build.tag_bump_and_push_release()
|
||||||
|
|
||||||
|
|
||||||
|
def release(project):
|
||||||
|
prepare(project)
|
||||||
|
tag(project)
|
||||||
|
|
||||||
|
|
||||||
|
def linttest(project, release_type):
|
||||||
|
build = get_devops_build(project)
|
||||||
|
build.update_release_type(release_type)
|
||||||
|
#test(project)
|
||||||
|
#lint(project)
|
||||||
|
```
|
||||||
|
|
||||||
|
### call the build for creating a major release
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pyb major
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example Usage for creating a release on forgejo / gitea & upload the generated artifacts
|
||||||
|
|
||||||
|
### build.py
|
||||||
|
|
||||||
|
```python
|
||||||
|
rom os import environ
|
||||||
|
from pybuilder.core import task, init
|
||||||
|
from ddadevops import *
|
||||||
|
|
||||||
|
name = 'my-project'
|
||||||
|
MODULE = 'my-module'
|
||||||
|
PROJECT_ROOT_PATH = '..'
|
||||||
|
|
||||||
|
@init
|
||||||
|
def initialize(project):
|
||||||
|
project.build_depends_on("ddadevops>=4.7.0")
|
||||||
|
|
||||||
|
input = {
|
||||||
|
"name": name,
|
||||||
|
"module": MODULE,
|
||||||
|
"stage": "notused",
|
||||||
|
"project_root_path": PROJECT_ROOT_PATH,
|
||||||
|
"build_types": [],
|
||||||
|
"mixin_types": ["RELEASE"],
|
||||||
|
"release_type": "MINOR",
|
||||||
|
"release_primary_build_file": "project.clj",
|
||||||
|
"release_secondary_build_files": ["package.json"],
|
||||||
|
"release_artifact_server_url": "https://repo.prod.meissa.de",
|
||||||
|
"release_organisation": "meissa",
|
||||||
|
"release_repository_name": "dda-devops-build",
|
||||||
|
"release_artifacts": ["target/doc.zip"],
|
||||||
|
}
|
||||||
|
build = ReleaseMixin(project, input)
|
||||||
|
build.initialize_build_dir()
|
||||||
|
|
||||||
|
@task
|
||||||
|
def publish_artifacts(project):
|
||||||
|
build = get_devops_build(project)
|
||||||
|
build.publish_artifacts()
|
||||||
```
|
```
|
||||||
|
|
||||||
### call the build
|
### call the build
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pyb prepare_release build publish after_publish
|
git checkout "4.7.0"
|
||||||
|
pyb publish_artifacts
|
||||||
```
|
```
|
||||||
|
|
|
@ -88,6 +88,15 @@ classDiagram
|
||||||
release_type
|
release_type
|
||||||
release_main_branch
|
release_main_branch
|
||||||
release_current_branch
|
release_current_branch
|
||||||
|
release_artifact_server_url
|
||||||
|
release_organisation
|
||||||
|
release_repository_name
|
||||||
|
release_artifact_token
|
||||||
|
}
|
||||||
|
class Artifact {
|
||||||
|
path_str
|
||||||
|
path()
|
||||||
|
type()
|
||||||
}
|
}
|
||||||
class Credentials {
|
class Credentials {
|
||||||
<<AggregateRoot>>
|
<<AggregateRoot>>
|
||||||
|
@ -130,6 +139,7 @@ classDiagram
|
||||||
TerraformDomain *-- "0..1" ProviderAws: providers
|
TerraformDomain *-- "0..1" ProviderAws: providers
|
||||||
Release o-- "0..1" BuildFile: primary_build_file
|
Release o-- "0..1" BuildFile: primary_build_file
|
||||||
Release o-- "0..n" BuildFile: secondary_build_files
|
Release o-- "0..n" BuildFile: secondary_build_files
|
||||||
|
Release "1" *-- "0..n" Artifact: release_artifacts
|
||||||
Release "1" *-- "1" Version: version
|
Release "1" *-- "1" Version: version
|
||||||
BuildFile *-- "1" Version: version
|
BuildFile *-- "1" Version: version
|
||||||
C4k *-- DnsRecord: dns_record
|
C4k *-- DnsRecord: dns_record
|
||||||
|
|
|
@ -6,7 +6,7 @@ from ddadevops import *
|
||||||
name = "ddadevops"
|
name = "ddadevops"
|
||||||
MODULE = "clj-cljs"
|
MODULE = "clj-cljs"
|
||||||
PROJECT_ROOT_PATH = "../.."
|
PROJECT_ROOT_PATH = "../.."
|
||||||
version = "4.3.2-dev"
|
version = "4.7.5-dev"
|
||||||
|
|
||||||
@init
|
@init
|
||||||
def initialize(project):
|
def initialize(project):
|
||||||
|
|
|
@ -23,7 +23,7 @@ function main() {
|
||||||
|
|
||||||
#install pyb
|
#install pyb
|
||||||
apt -qqy install python3 python3-pip git;
|
apt -qqy install python3 python3-pip git;
|
||||||
pip3 install pybuilder 'ddadevops>=4.2.0' deprecation dda-python-terraform boto3 pyyaml inflection --break-system-packages;
|
pip3 install pybuilder 'ddadevops>=4.7.0' deprecation dda-python-terraform boto3 pyyaml inflection --break-system-packages;
|
||||||
|
|
||||||
cleanupDocker
|
cleanupDocker
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ from ddadevops import *
|
||||||
name = "ddadevops"
|
name = "ddadevops"
|
||||||
MODULE = "clj"
|
MODULE = "clj"
|
||||||
PROJECT_ROOT_PATH = "../.."
|
PROJECT_ROOT_PATH = "../.."
|
||||||
version = "4.3.2-dev"
|
version = "4.7.5-dev"
|
||||||
|
|
||||||
@init
|
@init
|
||||||
def initialize(project):
|
def initialize(project):
|
||||||
|
|
|
@ -29,7 +29,7 @@ function main() {
|
||||||
|
|
||||||
#install pyb
|
#install pyb
|
||||||
apt -qqy install python3 python3-pip;
|
apt -qqy install python3 python3-pip;
|
||||||
pip3 install pybuilder 'ddadevops>=4.2.0' deprecation dda-python-terraform boto3 pyyaml inflection --break-system-packages;
|
pip3 install pybuilder 'ddadevops>=4.7.0' deprecation dda-python-terraform boto3 pyyaml inflection --break-system-packages;
|
||||||
|
|
||||||
cleanupDocker
|
cleanupDocker
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ from ddadevops import *
|
||||||
name = "ddadevops"
|
name = "ddadevops"
|
||||||
MODULE = "ddadevops"
|
MODULE = "ddadevops"
|
||||||
PROJECT_ROOT_PATH = "../.."
|
PROJECT_ROOT_PATH = "../.."
|
||||||
version = "4.3.2-dev"
|
version = "4.7.5-dev"
|
||||||
|
|
||||||
|
|
||||||
@init
|
@init
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
FROM python:3.10-alpine
|
FROM python:3.10-alpine
|
||||||
|
|
||||||
RUN set -eux;
|
RUN set -eux;
|
||||||
RUN apk add --no-cache python3 py3-pip openssl-dev bash git;
|
RUN apk add --no-cache python3 py3-pip openssl-dev bash git curl;
|
||||||
RUN python3 -m pip install -U pip;
|
RUN python3 -m pip install -U pip;
|
||||||
RUN pip3 install pybuilder ddadevops deprecation dda-python-terraform boto3 pyyaml inflection;
|
RUN pip3 install pybuilder ddadevops deprecation dda-python-terraform boto3 pyyaml inflection;
|
||||||
|
|
|
@ -6,7 +6,7 @@ from ddadevops import *
|
||||||
name = "ddadevops"
|
name = "ddadevops"
|
||||||
MODULE = "dind"
|
MODULE = "dind"
|
||||||
PROJECT_ROOT_PATH = "../.."
|
PROJECT_ROOT_PATH = "../.."
|
||||||
version = "4.3.2-dev"
|
version = "4.7.5-dev"
|
||||||
|
|
||||||
|
|
||||||
@init
|
@init
|
||||||
|
|
|
@ -6,7 +6,7 @@ from ddadevops import *
|
||||||
name = "ddadevops"
|
name = "ddadevops"
|
||||||
MODULE = "python"
|
MODULE = "python"
|
||||||
PROJECT_ROOT_PATH = "../.."
|
PROJECT_ROOT_PATH = "../.."
|
||||||
version = "4.3.2-dev"
|
version = "4.7.5-dev"
|
||||||
|
|
||||||
|
|
||||||
@init
|
@init
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
FROM python:3.10-alpine
|
FROM python:3.10-alpine
|
||||||
|
|
||||||
RUN set -eux;
|
RUN set -eux;
|
||||||
RUN apk add --no-cache build-base rust python3 python3-dev py3-pip py3-setuptools py3-wheel libffi-dev openssl-dev cargo bash git;
|
RUN apk add --no-cache build-base rust python3 python3-dev py3-pip py3-setuptools py3-wheel libffi-dev openssl-dev cargo bash git curl;
|
||||||
RUN python3 -m pip install -U pip;
|
RUN python3 -m pip install -U pip;
|
||||||
RUN pip3 install pybuilder ddadevops deprecation dda-python-terraform boto3 pyyaml inflection;
|
RUN pip3 install pybuilder ddadevops deprecation dda-python-terraform boto3 pyyaml inflection;
|
||||||
RUN pip3 install coverage flake8 flake8-polyfill mypy mypy-extensions pycodestyle pyflakes pylint pytest pytest-cov pytest-datafiles types-setuptools types-PyYAML;
|
RUN pip3 install coverage flake8 flake8-polyfill mypy mypy-extensions pycodestyle pyflakes pylint pytest pytest-cov pytest-datafiles types-setuptools types-PyYAML;
|
||||||
|
|
|
@ -1,18 +1,26 @@
|
||||||
|
import json
|
||||||
from typing import List
|
from typing import List
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from ..infrastructure import GitApi, BuildFileRepository
|
from ..infrastructure import GitApi, ArtifactDeploymentApi, BuildFileRepository
|
||||||
from ..domain import Version, Release, ReleaseType
|
from ..domain import Version, Release, ReleaseType, Artifact
|
||||||
|
|
||||||
|
|
||||||
class ReleaseService:
|
class ReleaseService:
|
||||||
def __init__(self, git_api: GitApi, build_file_repository: BuildFileRepository):
|
def __init__(
|
||||||
|
self,
|
||||||
|
git_api: GitApi,
|
||||||
|
artifact_deployment_api: ArtifactDeploymentApi,
|
||||||
|
build_file_repository: BuildFileRepository,
|
||||||
|
):
|
||||||
self.git_api = git_api
|
self.git_api = git_api
|
||||||
|
self.artifact_deployment_api = artifact_deployment_api
|
||||||
self.build_file_repository = build_file_repository
|
self.build_file_repository = build_file_repository
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def prod(cls, base_dir: str):
|
def prod(cls, base_dir: str):
|
||||||
return cls(
|
return cls(
|
||||||
GitApi(),
|
GitApi(),
|
||||||
|
ArtifactDeploymentApi(),
|
||||||
BuildFileRepository(base_dir),
|
BuildFileRepository(base_dir),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -53,6 +61,41 @@ class ReleaseService:
|
||||||
)
|
)
|
||||||
self.git_api.push_follow_tags()
|
self.git_api.push_follow_tags()
|
||||||
|
|
||||||
|
def publish_artifacts(self, release: Release):
|
||||||
|
token = str(release.release_artifact_token)
|
||||||
|
release_id = self.__parse_forgejo_release_id__(
|
||||||
|
self.artifact_deployment_api.create_forgejo_release(
|
||||||
|
release.forgejo_release_api_endpoint(),
|
||||||
|
release.version.to_string(),
|
||||||
|
token,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
artifacts_sums = []
|
||||||
|
for artifact in release.release_artifacts:
|
||||||
|
sha256 = self.artifact_deployment_api.calculate_sha256(artifact.path())
|
||||||
|
sha512 = self.artifact_deployment_api.calculate_sha512(artifact.path())
|
||||||
|
artifacts_sums += [Artifact(sha256), Artifact(sha512)]
|
||||||
|
|
||||||
|
artifacts = release.release_artifacts + artifacts_sums
|
||||||
|
print(artifacts)
|
||||||
|
for artifact in artifacts:
|
||||||
|
print(str)
|
||||||
|
self.artifact_deployment_api.add_asset_to_release(
|
||||||
|
release.forgejo_release_asset_api_endpoint(release_id),
|
||||||
|
artifact.path(),
|
||||||
|
artifact.type(),
|
||||||
|
token,
|
||||||
|
)
|
||||||
|
|
||||||
|
def __parse_forgejo_release_id__(self, release_response: str) -> int:
|
||||||
|
parsed = json.loads(release_response)
|
||||||
|
try:
|
||||||
|
result = parsed["id"]
|
||||||
|
except:
|
||||||
|
raise RuntimeError(str(parsed))
|
||||||
|
return result
|
||||||
|
|
||||||
def __set_version_and_commit__(
|
def __set_version_and_commit__(
|
||||||
self, version: Version, build_file_ids: List[str], message: str
|
self, version: Version, build_file_ids: List[str], message: str
|
||||||
):
|
):
|
||||||
|
|
|
@ -1,56 +0,0 @@
|
||||||
from pybuilder.core import Project
|
|
||||||
from .devops_build import DevopsBuild
|
|
||||||
|
|
||||||
# """
|
|
||||||
# Functional Req:
|
|
||||||
|
|
||||||
# General process for deploying prebuilt (meissa) binaries to our own repo server.
|
|
||||||
|
|
||||||
# [-1]
|
|
||||||
# Building is handled by other entities
|
|
||||||
# is another pybuilder task
|
|
||||||
# the binary is reachable with devops.build_path()
|
|
||||||
# we might need to establish a "build" that does lein builds for us
|
|
||||||
# we might need to establish a "build" that does gradlew build for us
|
|
||||||
# same for all other projects that produce binaries
|
|
||||||
# currently the c4k_build.py just creates the auth and config yamls
|
|
||||||
|
|
||||||
# [0]
|
|
||||||
# get artifact deployment url
|
|
||||||
# Base url: https://repo.prod.meissa.de/api/v1/repos/
|
|
||||||
# Changeable: /meissa/provs/
|
|
||||||
# persitent suffix to url: releases
|
|
||||||
# name is accessible from input
|
|
||||||
|
|
||||||
# [1]
|
|
||||||
# get release token
|
|
||||||
# could be an api token for repo.prod.meissa.de
|
|
||||||
# credential mapping as described in the docs
|
|
||||||
|
|
||||||
# [2]
|
|
||||||
# get release tag
|
|
||||||
# is the version of the project
|
|
||||||
# get from gitApi
|
|
||||||
|
|
||||||
# [3]
|
|
||||||
# post a json message containting [2] to [0], watching stdout for answers
|
|
||||||
# authorized by [1]
|
|
||||||
# validate if [3] was successful by reading stdout
|
|
||||||
# or create error message containing ID of release
|
|
||||||
|
|
||||||
# [4]
|
|
||||||
# get release-id from stdout of [3]
|
|
||||||
# print release-id
|
|
||||||
|
|
||||||
# [5]
|
|
||||||
# generate sha256 sums & generate sha512 sums of results of [-1]
|
|
||||||
|
|
||||||
# [6]
|
|
||||||
# push results of [-1] & [5] to [0]/[4]
|
|
||||||
|
|
||||||
# """
|
|
||||||
|
|
||||||
|
|
||||||
class ArtifactDeploymentMixin(DevopsBuild):
|
|
||||||
def __init__(self, project: Project, inp: dict):
|
|
||||||
super().__init__(project, inp)
|
|
|
@ -17,6 +17,7 @@ from .provider_hetzner import Hetzner
|
||||||
from .provider_aws import Aws
|
from .provider_aws import Aws
|
||||||
from .provs_k3s import K3s
|
from .provs_k3s import K3s
|
||||||
from .release import Release
|
from .release import Release
|
||||||
|
from .artifact import Artifact
|
||||||
from .credentials import Credentials, CredentialMapping, GopassType
|
from .credentials import Credentials, CredentialMapping, GopassType
|
||||||
from .version import Version
|
from .version import Version
|
||||||
from .build_file import BuildFileType, BuildFile
|
from .build_file import BuildFileType, BuildFile
|
||||||
|
|
46
src/main/python/ddadevops/domain/artifact.py
Normal file
46
src/main/python/ddadevops/domain/artifact.py
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
from enum import Enum
|
||||||
|
from pathlib import Path
|
||||||
|
from .common import (
|
||||||
|
Validateable,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ArtifactType(Enum):
|
||||||
|
TEXT = 0
|
||||||
|
JAR = 1
|
||||||
|
|
||||||
|
|
||||||
|
class Artifact(Validateable):
|
||||||
|
def __init__(self, path: str):
|
||||||
|
self.path_str = path
|
||||||
|
|
||||||
|
def path(self) -> Path:
|
||||||
|
return Path(self.path_str)
|
||||||
|
|
||||||
|
def type(self) -> str:
|
||||||
|
suffix = self.path().suffix
|
||||||
|
match suffix:
|
||||||
|
case ".jar":
|
||||||
|
return "application/x-java-archive"
|
||||||
|
case ".js":
|
||||||
|
return "application/x-javascript"
|
||||||
|
case _:
|
||||||
|
return "text/plain"
|
||||||
|
|
||||||
|
def validate(self):
|
||||||
|
result = []
|
||||||
|
result += self.__validate_is_not_empty__("path_str")
|
||||||
|
try:
|
||||||
|
Path(self.path_str)
|
||||||
|
except Exception as e:
|
||||||
|
result += [f"path was not a valid: {e}"]
|
||||||
|
return result
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return str(self.path())
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return other and self.__str__() == other.__str__()
|
||||||
|
|
||||||
|
def __hash__(self) -> int:
|
||||||
|
return self.__str__().__hash__()
|
|
@ -8,7 +8,7 @@ from .provider_digitalocean import Digitalocean
|
||||||
from .provider_hetzner import Hetzner
|
from .provider_hetzner import Hetzner
|
||||||
from .c4k import C4k
|
from .c4k import C4k
|
||||||
from .image import Image
|
from .image import Image
|
||||||
from .release import ReleaseType
|
from .release import ReleaseType, Release
|
||||||
from ..infrastructure import BuildFileRepository, CredentialsApi, EnvironmentApi, GitApi
|
from ..infrastructure import BuildFileRepository, CredentialsApi, EnvironmentApi, GitApi
|
||||||
|
|
||||||
|
|
||||||
|
@ -69,6 +69,7 @@ class InitService:
|
||||||
Path(primary_build_file_id)
|
Path(primary_build_file_id)
|
||||||
)
|
)
|
||||||
version = primary_build_file.get_version()
|
version = primary_build_file.get_version()
|
||||||
|
default_mappings += Release.get_mapping_default()
|
||||||
|
|
||||||
credentials = Credentials(inp, default_mappings)
|
credentials = Credentials(inp, default_mappings)
|
||||||
authorization = self.authorization(credentials)
|
authorization = self.authorization(credentials)
|
||||||
|
@ -111,9 +112,8 @@ class InitService:
|
||||||
result = {}
|
result = {}
|
||||||
for name in credentials.mappings.keys():
|
for name in credentials.mappings.keys():
|
||||||
mapping = credentials.mappings[name]
|
mapping = credentials.mappings[name]
|
||||||
env_value = self.environment_api.get(mapping.name_for_environment())
|
if self.environment_api.is_defined(mapping.name_for_environment()):
|
||||||
if env_value:
|
result[name] = self.environment_api.get(mapping.name_for_environment())
|
||||||
result[name] = env_value
|
|
||||||
else:
|
else:
|
||||||
if mapping.gopass_type() == GopassType.FIELD:
|
if mapping.gopass_type() == GopassType.FIELD:
|
||||||
result[name] = self.credentials_api.gopass_field_from_path(
|
result[name] = self.credentials_api.gopass_field_from_path(
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from typing import Optional, List
|
from typing import Optional, List, Dict
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from .common import (
|
from .common import (
|
||||||
Validateable,
|
Validateable,
|
||||||
|
@ -7,6 +7,9 @@ from .common import (
|
||||||
from .version import (
|
from .version import (
|
||||||
Version,
|
Version,
|
||||||
)
|
)
|
||||||
|
from .artifact import (
|
||||||
|
Artifact,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Release(Validateable):
|
class Release(Validateable):
|
||||||
|
@ -21,6 +24,13 @@ class Release(Validateable):
|
||||||
"release_secondary_build_files", []
|
"release_secondary_build_files", []
|
||||||
)
|
)
|
||||||
self.version = version
|
self.version = version
|
||||||
|
self.release_artifact_server_url = inp.get("release_artifact_server_url")
|
||||||
|
self.release_organisation = inp.get("release_organisation")
|
||||||
|
self.release_repository_name = inp.get("release_repository_name")
|
||||||
|
self.release_artifact_token = inp.get("release_artifact_token")
|
||||||
|
self.release_artifacts = []
|
||||||
|
for a in inp.get("release_artifacts", []):
|
||||||
|
self.release_artifacts.append(Artifact(a))
|
||||||
|
|
||||||
def update_release_type(self, release_type: ReleaseType):
|
def update_release_type(self, release_type: ReleaseType):
|
||||||
self.release_type = release_type
|
self.release_type = release_type
|
||||||
|
@ -53,10 +63,44 @@ class Release(Validateable):
|
||||||
and self.release_type != ReleaseType.NONE
|
and self.release_type != ReleaseType.NONE
|
||||||
and self.release_main_branch != self.release_current_branch
|
and self.release_main_branch != self.release_current_branch
|
||||||
):
|
):
|
||||||
result.append(f"Releases are allowed only on {self.release_main_branch}")
|
result.append(
|
||||||
|
f"Releases are allowed only on {self.release_main_branch}"
|
||||||
|
)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def validate_for_artifact(self):
|
||||||
|
result = []
|
||||||
|
result += self.__validate_is_not_empty__("release_artifact_server_url")
|
||||||
|
result += self.__validate_is_not_empty__("release_organisation")
|
||||||
|
result += self.__validate_is_not_empty__("release_repository_name")
|
||||||
|
result += self.__validate_is_not_empty__("release_artifact_token")
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def build_files(self) -> List[str]:
|
def build_files(self) -> List[str]:
|
||||||
result = [self.release_primary_build_file]
|
result = [self.release_primary_build_file]
|
||||||
result += self.release_secondary_build_files
|
result += self.release_secondary_build_files
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def forgejo_release_api_endpoint(self) -> str:
|
||||||
|
validation = self.validate_for_artifact()
|
||||||
|
if validation != []:
|
||||||
|
raise RuntimeError(f"not valid for creating artifacts: {validation}")
|
||||||
|
|
||||||
|
server_url = self.release_artifact_server_url.removeprefix("/").removesuffix(
|
||||||
|
"/"
|
||||||
|
)
|
||||||
|
organisation = self.release_organisation.removeprefix("/").removesuffix("/")
|
||||||
|
repository = self.release_repository_name.removeprefix("/").removesuffix("/")
|
||||||
|
return f"{server_url}/api/v1/repos/{organisation}/{repository}/releases"
|
||||||
|
|
||||||
|
def forgejo_release_asset_api_endpoint(self, release_id: int) -> str:
|
||||||
|
return f"{self.forgejo_release_api_endpoint()}/{release_id}/assets"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_mapping_default(cls) -> List[Dict[str, str]]:
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"gopass_path": "server/meissa/repo/buero-rw",
|
||||||
|
"name": "release_artifact_token",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
|
@ -32,12 +32,6 @@ class Version(Validateable):
|
||||||
self.snapshot_suffix = snapshot_suffix
|
self.snapshot_suffix = snapshot_suffix
|
||||||
self.default_snapshot_suffix = default_snapshot_suffix
|
self.default_snapshot_suffix = default_snapshot_suffix
|
||||||
|
|
||||||
def __eq__(self, other):
|
|
||||||
return other and self.to_string() == other.to_string()
|
|
||||||
|
|
||||||
def __hash__(self) -> int:
|
|
||||||
return self.to_string().__hash__()
|
|
||||||
|
|
||||||
def is_snapshot(self):
|
def is_snapshot(self):
|
||||||
return self.snapshot_suffix is not None
|
return self.snapshot_suffix is not None
|
||||||
|
|
||||||
|
@ -139,3 +133,9 @@ class Version(Validateable):
|
||||||
snapshot_suffix=None,
|
snapshot_suffix=None,
|
||||||
version_str=None,
|
version_str=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return other and self.to_string() == other.to_string()
|
||||||
|
|
||||||
|
def __hash__(self) -> int:
|
||||||
|
return self.to_string().__hash__()
|
||||||
|
|
|
@ -7,5 +7,6 @@ from .infrastructure import (
|
||||||
CredentialsApi,
|
CredentialsApi,
|
||||||
GitApi,
|
GitApi,
|
||||||
TerraformApi,
|
TerraformApi,
|
||||||
|
ArtifactDeploymentApi,
|
||||||
)
|
)
|
||||||
from .repository import DevopsRepository, BuildFileRepository
|
from .repository import DevopsRepository, BuildFileRepository
|
||||||
|
|
|
@ -58,23 +58,21 @@ class ImageApi:
|
||||||
)
|
)
|
||||||
|
|
||||||
def drun(self, name: str):
|
def drun(self, name: str):
|
||||||
self.execution_api.execute_live(
|
run(
|
||||||
f'docker run -it --entrypoint="" {name} /bin/bash'
|
f'docker run -it --entrypoint="" {name} /bin/bash',
|
||||||
|
shell=True,
|
||||||
|
check=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
def dockerhub_login(self, username: str, password: str):
|
def dockerhub_login(self, username: str, password: str):
|
||||||
self.execution_api.execute_secure(
|
self.execution_api.execute_secure(
|
||||||
f"docker login --username {username} --password {password}",
|
f"docker login --username {username} --password {password}",
|
||||||
"docker login --username ***** --password *****"
|
"docker login --username ***** --password *****",
|
||||||
)
|
)
|
||||||
|
|
||||||
def dockerhub_publish(self, name: str, username: str, tag: str):
|
def dockerhub_publish(self, name: str, username: str, tag: str):
|
||||||
self.execution_api.execute_live(
|
self.execution_api.execute_live(f"docker tag {name} {username}/{name}:{tag}")
|
||||||
f"docker tag {name} {username}/{name}:{tag}"
|
self.execution_api.execute_live(f"docker push {username}/{name}:{tag}")
|
||||||
)
|
|
||||||
self.execution_api.execute_live(
|
|
||||||
f"docker push {username}/{name}:{tag}"
|
|
||||||
)
|
|
||||||
|
|
||||||
def test(self, name: str, path: Path):
|
def test(self, name: str, path: Path):
|
||||||
self.execution_api.execute_live(
|
self.execution_api.execute_live(
|
||||||
|
@ -95,14 +93,24 @@ class ExecutionApi:
|
||||||
check=check,
|
check=check,
|
||||||
stdout=PIPE,
|
stdout=PIPE,
|
||||||
stderr=PIPE,
|
stderr=PIPE,
|
||||||
text=True).stdout
|
text=True,
|
||||||
|
).stdout
|
||||||
output = output.rstrip()
|
output = output.rstrip()
|
||||||
except CalledProcessError as exc:
|
except CalledProcessError as exc:
|
||||||
print(f"Command failed with code: {exc.returncode} and message: {exc.stderr}")
|
print(
|
||||||
|
f"Command failed with code: {exc.returncode} and message: {exc.stderr}"
|
||||||
|
)
|
||||||
raise exc
|
raise exc
|
||||||
return output
|
return output
|
||||||
|
|
||||||
def execute_secure(self, command: str, sanitized_command: str, dry_run=False, shell=True, check=True):
|
def execute_secure(
|
||||||
|
self,
|
||||||
|
command: str,
|
||||||
|
sanitized_command: str,
|
||||||
|
dry_run=False,
|
||||||
|
shell=True,
|
||||||
|
check=True,
|
||||||
|
):
|
||||||
try:
|
try:
|
||||||
output = self.execute(command, dry_run, shell, check)
|
output = self.execute(command, dry_run, shell, check)
|
||||||
return output
|
return output
|
||||||
|
@ -128,6 +136,9 @@ class EnvironmentApi:
|
||||||
def get(self, key):
|
def get(self, key):
|
||||||
return environ.get(key)
|
return environ.get(key)
|
||||||
|
|
||||||
|
def is_defined(self, key):
|
||||||
|
return key in environ
|
||||||
|
|
||||||
|
|
||||||
class CredentialsApi:
|
class CredentialsApi:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -206,3 +217,53 @@ class GitApi:
|
||||||
|
|
||||||
class TerraformApi:
|
class TerraformApi:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ArtifactDeploymentApi:
|
||||||
|
def __init__(self):
|
||||||
|
self.execution_api = ExecutionApi()
|
||||||
|
|
||||||
|
def create_forgejo_release(self, api_endpoint_url: str, tag: str, token: str):
|
||||||
|
command = (
|
||||||
|
f'curl -X "POST" "{api_endpoint_url}" '
|
||||||
|
+ ' -H "accept: application/json" -H "Content-Type: application/json"'
|
||||||
|
+ f' -d \'{{ "body": "Provides files for release {tag}", "tag_name": "{tag}"}}\''
|
||||||
|
) # noqa: E501
|
||||||
|
print(command + ' -H "Authorization: token xxxx"')
|
||||||
|
return self.execution_api.execute_secure(
|
||||||
|
command=command + f' -H "Authorization: token {token}"',
|
||||||
|
sanitized_command=command + ' -H "Authorization: token xxxx"',
|
||||||
|
)
|
||||||
|
|
||||||
|
def add_asset_to_release(
|
||||||
|
self,
|
||||||
|
api_endpoint_url: str,
|
||||||
|
attachment: Path,
|
||||||
|
attachment_type: str,
|
||||||
|
token: str,
|
||||||
|
):
|
||||||
|
command = (
|
||||||
|
f'curl -X "POST" "{api_endpoint_url}"'
|
||||||
|
+ ' -H "accept: application/json"'
|
||||||
|
+ ' -H "Content-Type: multipart/form-data"'
|
||||||
|
+ f' -F "attachment=@{attachment};type={attachment_type}"'
|
||||||
|
) # noqa: E501
|
||||||
|
print(command + ' -H "Authorization: token xxxx"')
|
||||||
|
return self.execution_api.execute_secure(
|
||||||
|
command=command + f' -H "Authorization: token {token}"',
|
||||||
|
sanitized_command=command + ' -H "Authorization: token xxxx"',
|
||||||
|
)
|
||||||
|
|
||||||
|
def calculate_sha256(self, path: Path):
|
||||||
|
shasum = f"{path}.sha256"
|
||||||
|
self.execution_api.execute(
|
||||||
|
f"sha256sum {path} > {shasum}",
|
||||||
|
)
|
||||||
|
return shasum
|
||||||
|
|
||||||
|
def calculate_sha512(self, path: Path):
|
||||||
|
shasum = f"{path}.sha512"
|
||||||
|
self.execution_api.execute(
|
||||||
|
f"sha512sum {path} > {shasum}",
|
||||||
|
)
|
||||||
|
return shasum
|
||||||
|
|
|
@ -26,3 +26,8 @@ class ReleaseMixin(DevopsBuild):
|
||||||
devops = self.devops_repo.get_devops(self.project)
|
devops = self.devops_repo.get_devops(self.project)
|
||||||
release = devops.mixins[MixinType.RELEASE]
|
release = devops.mixins[MixinType.RELEASE]
|
||||||
self.release_service.tag_bump_and_push_release(release)
|
self.release_service.tag_bump_and_push_release(release)
|
||||||
|
|
||||||
|
def publish_artifacts(self):
|
||||||
|
devops = self.devops_repo.get_devops(self.project)
|
||||||
|
release = devops.mixins[MixinType.RELEASE]
|
||||||
|
self.release_service.publish_artifacts(release)
|
||||||
|
|
|
@ -7,12 +7,16 @@ from src.main.python.ddadevops.domain import (
|
||||||
from src.test.python.domain.helper import (
|
from src.test.python.domain.helper import (
|
||||||
BuildFileRepositoryMock,
|
BuildFileRepositoryMock,
|
||||||
GitApiMock,
|
GitApiMock,
|
||||||
|
ArtifactDeploymentApiMock,
|
||||||
build_devops,
|
build_devops,
|
||||||
)
|
)
|
||||||
from src.main.python.ddadevops.application import ReleaseService
|
from src.main.python.ddadevops.application import ReleaseService
|
||||||
|
|
||||||
|
|
||||||
def test_sould_update_release_type():
|
def test_sould_update_release_type():
|
||||||
sut = ReleaseService(GitApiMock(), BuildFileRepositoryMock("build.py"))
|
sut = ReleaseService(
|
||||||
|
GitApiMock(), ArtifactDeploymentApiMock(), BuildFileRepositoryMock("build.py")
|
||||||
|
)
|
||||||
devops = build_devops({})
|
devops = build_devops({})
|
||||||
release = devops.mixins[MixinType.RELEASE]
|
release = devops.mixins[MixinType.RELEASE]
|
||||||
sut.update_release_type(release, "MAJOR")
|
sut.update_release_type(release, "MAJOR")
|
||||||
|
@ -20,3 +24,40 @@ def test_sould_update_release_type():
|
||||||
|
|
||||||
with pytest.raises(Exception):
|
with pytest.raises(Exception):
|
||||||
sut.update_release_type(release, "NOT_EXISTING")
|
sut.update_release_type(release, "NOT_EXISTING")
|
||||||
|
|
||||||
|
|
||||||
|
def test_sould_publish_artifacts():
|
||||||
|
mock = ArtifactDeploymentApiMock(release='{"id": 2345}')
|
||||||
|
sut = ReleaseService(GitApiMock(), mock, BuildFileRepositoryMock())
|
||||||
|
devops = build_devops(
|
||||||
|
{
|
||||||
|
"release_artifacts": ["target/art"],
|
||||||
|
"release_artifact_server_url": "http://repo.test/",
|
||||||
|
"release_organisation": "orga",
|
||||||
|
"release_repository_name": "repo",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
release = devops.mixins[MixinType.RELEASE]
|
||||||
|
sut.publish_artifacts(release)
|
||||||
|
assert "http://repo.test/api/v1/repos/orga/repo/releases/2345/assets" == mock.add_asset_to_release_api_endpoint
|
||||||
|
|
||||||
|
def test_sould_throw_exception_if_there_was_an_error_in_publish_artifacts():
|
||||||
|
devops = build_devops(
|
||||||
|
{
|
||||||
|
"release_artifacts": ["target/art"],
|
||||||
|
"release_artifact_server_url": "http://repo.test/",
|
||||||
|
"release_organisation": "orga",
|
||||||
|
"release_repository_name": "repo",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
release = devops.mixins[MixinType.RELEASE]
|
||||||
|
|
||||||
|
with pytest.raises(Exception):
|
||||||
|
mock = ArtifactDeploymentApiMock(release='')
|
||||||
|
sut = ReleaseService(GitApiMock(), mock, BuildFileRepositoryMock())
|
||||||
|
sut.publish_artifacts(release)
|
||||||
|
|
||||||
|
with pytest.raises(Exception):
|
||||||
|
mock = ArtifactDeploymentApiMock(release='{"message": "there was an error", "url":"some-url"}')
|
||||||
|
sut = ReleaseService(GitApiMock(), mock, BuildFileRepositoryMock())
|
||||||
|
sut.publish_artifacts(release)
|
||||||
|
|
|
@ -53,6 +53,11 @@ def devops_config(overrides: dict) -> dict:
|
||||||
"release_current_branch": "my_feature",
|
"release_current_branch": "my_feature",
|
||||||
"release_primary_build_file": "./package.json",
|
"release_primary_build_file": "./package.json",
|
||||||
"release_secondary_build_file": [],
|
"release_secondary_build_file": [],
|
||||||
|
"release_artifacts": [],
|
||||||
|
"release_artifact_token": "release_artifact_token",
|
||||||
|
"release_artifact_server_url": None,
|
||||||
|
"release_organisation": None,
|
||||||
|
"release_repository_name": None,
|
||||||
"credentials_mappings": [
|
"credentials_mappings": [
|
||||||
{
|
{
|
||||||
"gopass_path": "a/path",
|
"gopass_path": "a/path",
|
||||||
|
@ -99,6 +104,9 @@ class EnvironmentApiMock:
|
||||||
def get(self, key):
|
def get(self, key):
|
||||||
return self.mappings.get(key, None)
|
return self.mappings.get(key, None)
|
||||||
|
|
||||||
|
def is_defined(self, key):
|
||||||
|
return key in self.mappings
|
||||||
|
|
||||||
|
|
||||||
class CredentialsApiMock:
|
class CredentialsApiMock:
|
||||||
def __init__(self, mappings):
|
def __init__(self, mappings):
|
||||||
|
@ -150,3 +158,28 @@ class GitApiMock:
|
||||||
|
|
||||||
def checkout(self, branch: str):
|
def checkout(self, branch: str):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ArtifactDeploymentApiMock:
|
||||||
|
def __init__(self, release=""):
|
||||||
|
self.release = release
|
||||||
|
self.create_forgejo_release_count = 0
|
||||||
|
self.add_asset_to_release_count = 0
|
||||||
|
self.add_asset_to_release_api_endpoint = ""
|
||||||
|
|
||||||
|
def create_forgejo_release(self, api_endpoint: str, tag: str, token: str):
|
||||||
|
self.create_forgejo_release_count += 1
|
||||||
|
return self.release
|
||||||
|
|
||||||
|
def add_asset_to_release(
|
||||||
|
self, api_endpoint: str, attachment: str, attachment_type: str, token: str
|
||||||
|
):
|
||||||
|
self.add_asset_to_release_api_endpoint = api_endpoint
|
||||||
|
self.add_asset_to_release_count += 1
|
||||||
|
pass
|
||||||
|
|
||||||
|
def calculate_sha256(self, path: Path):
|
||||||
|
return f"{path}.sha256"
|
||||||
|
|
||||||
|
def calculate_sha512(self, path: Path):
|
||||||
|
return f"{path}.sha512"
|
||||||
|
|
32
src/test/python/domain/test_artifact.py
Normal file
32
src/test/python/domain/test_artifact.py
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import pytest
|
||||||
|
from pybuilder.core import Project
|
||||||
|
from pathlib import Path
|
||||||
|
from src.main.python.ddadevops.domain import (
|
||||||
|
Validateable,
|
||||||
|
DnsRecord,
|
||||||
|
Devops,
|
||||||
|
BuildType,
|
||||||
|
MixinType,
|
||||||
|
Artifact,
|
||||||
|
Image,
|
||||||
|
)
|
||||||
|
from .helper import build_devops, devops_config
|
||||||
|
|
||||||
|
|
||||||
|
def test_sould_validate_release():
|
||||||
|
sut = Artifact("x")
|
||||||
|
assert sut.is_valid()
|
||||||
|
|
||||||
|
sut = Artifact(None)
|
||||||
|
assert not sut.is_valid()
|
||||||
|
|
||||||
|
def test_should_calculate_type():
|
||||||
|
sut = Artifact("x.jar")
|
||||||
|
assert "application/x-java-archive" == sut.type()
|
||||||
|
|
||||||
|
sut = Artifact("x.js")
|
||||||
|
assert "application/x-javascript" == sut.type()
|
||||||
|
|
||||||
|
sut = Artifact("x.jar.sha256")
|
||||||
|
assert "text/plain" == sut.type()
|
||||||
|
|
|
@ -4,6 +4,7 @@ from src.main.python.ddadevops.domain import (
|
||||||
Version,
|
Version,
|
||||||
BuildType,
|
BuildType,
|
||||||
MixinType,
|
MixinType,
|
||||||
|
Artifact,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -50,6 +51,7 @@ def test_devops_creation():
|
||||||
assert sut is not None
|
assert sut is not None
|
||||||
assert sut.specialized_builds[BuildType.C4K] is not None
|
assert sut.specialized_builds[BuildType.C4K] is not None
|
||||||
|
|
||||||
|
def test_release_devops_creation():
|
||||||
sut = DevopsFactory().build_devops(
|
sut = DevopsFactory().build_devops(
|
||||||
{
|
{
|
||||||
"stage": "test",
|
"stage": "test",
|
||||||
|
@ -67,6 +69,30 @@ def test_devops_creation():
|
||||||
assert sut is not None
|
assert sut is not None
|
||||||
assert sut.mixins[MixinType.RELEASE] is not None
|
assert sut.mixins[MixinType.RELEASE] is not None
|
||||||
|
|
||||||
|
sut = DevopsFactory().build_devops(
|
||||||
|
{
|
||||||
|
"stage": "test",
|
||||||
|
"name": "mybuild",
|
||||||
|
"module": "test_image",
|
||||||
|
"project_root_path": "../../..",
|
||||||
|
"build_types": [],
|
||||||
|
"mixin_types": ["RELEASE"],
|
||||||
|
"release_main_branch": "main",
|
||||||
|
"release_current_branch": "my_feature",
|
||||||
|
"release_config_file": "project.clj",
|
||||||
|
"release_artifacts": ["x.jar"],
|
||||||
|
"release_artifact_token": "y",
|
||||||
|
"release_artifact_server_url": "https://repo.prod.meissa.de",
|
||||||
|
"release_organisation": "meissa",
|
||||||
|
"release_repository_name": "provs",
|
||||||
|
},
|
||||||
|
Version.from_str("1.0.0", "SNAPSHOT"),
|
||||||
|
)
|
||||||
|
|
||||||
|
release = sut.mixins[MixinType.RELEASE]
|
||||||
|
assert release is not None
|
||||||
|
assert Artifact("x.jar") == release.release_artifacts[0]
|
||||||
|
|
||||||
|
|
||||||
def test_on_merge_input_should_win():
|
def test_on_merge_input_should_win():
|
||||||
sut = DevopsFactory()
|
sut = DevopsFactory()
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import pytest
|
||||||
from pybuilder.core import Project
|
from pybuilder.core import Project
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from src.main.python.ddadevops.domain import (
|
from src.main.python.ddadevops.domain import (
|
||||||
|
@ -61,3 +62,74 @@ def test_sould_calculate_build_files():
|
||||||
Version.from_str("1.3.1-SNAPSHOT", "SNAPSHOT"),
|
Version.from_str("1.3.1-SNAPSHOT", "SNAPSHOT"),
|
||||||
)
|
)
|
||||||
assert ["project.clj", "package.json"] == sut.build_files()
|
assert ["project.clj", "package.json"] == sut.build_files()
|
||||||
|
|
||||||
|
|
||||||
|
def test_should_calculate_forgejo_release_api_endpoint():
|
||||||
|
sut = Release(
|
||||||
|
devops_config(
|
||||||
|
{
|
||||||
|
"release_artifacts": [],
|
||||||
|
"release_artifact_token": "y",
|
||||||
|
"release_artifact_server_url": "https://repo.prod.meissa.de",
|
||||||
|
"release_organisation": "meissa",
|
||||||
|
"release_repository_name": "provs",
|
||||||
|
}
|
||||||
|
),
|
||||||
|
Version.from_str("1.3.1-SNAPSHOT", "SNAPSHOT"),
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
"https://repo.prod.meissa.de/api/v1/repos/meissa/provs/releases"
|
||||||
|
== sut.forgejo_release_api_endpoint()
|
||||||
|
)
|
||||||
|
|
||||||
|
sut = Release(
|
||||||
|
devops_config(
|
||||||
|
{
|
||||||
|
"release_artifacts": ["x"],
|
||||||
|
"release_artifact_token": "y",
|
||||||
|
"release_artifact_server_url": "https://repo.prod.meissa.de/",
|
||||||
|
"release_organisation": "/meissa/",
|
||||||
|
"release_repository_name": "provs",
|
||||||
|
}
|
||||||
|
),
|
||||||
|
Version.from_str("1.3.1-SNAPSHOT", "SNAPSHOT"),
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
"https://repo.prod.meissa.de/api/v1/repos/meissa/provs/releases"
|
||||||
|
== sut.forgejo_release_api_endpoint()
|
||||||
|
)
|
||||||
|
assert(
|
||||||
|
"/meissa/"
|
||||||
|
== sut.release_organisation
|
||||||
|
)
|
||||||
|
|
||||||
|
with pytest.raises(Exception):
|
||||||
|
sut = Release(
|
||||||
|
devops_config(
|
||||||
|
{
|
||||||
|
"release_artifact_server_url": "https://repo.prod.meissa.de",
|
||||||
|
"release_organisation": None,
|
||||||
|
"release_repository_name": "provs",
|
||||||
|
}
|
||||||
|
),
|
||||||
|
Version.from_str("1.3.1-SNAPSHOT", "SNAPSHOT"),
|
||||||
|
)
|
||||||
|
sut.forgejo_release_api_endpoint()
|
||||||
|
|
||||||
|
def test_should_calculate_forgejo_release_asset_api_endpoint():
|
||||||
|
sut = Release(
|
||||||
|
devops_config(
|
||||||
|
{
|
||||||
|
"release_artifacts": ["x"],
|
||||||
|
"release_artifact_token": "y",
|
||||||
|
"release_artifact_server_url": "https://repo.prod.meissa.de",
|
||||||
|
"release_organisation": "meissa",
|
||||||
|
"release_repository_name": "provs",
|
||||||
|
}
|
||||||
|
),
|
||||||
|
Version.from_str("1.3.1-SNAPSHOT", "SNAPSHOT"),
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
"https://repo.prod.meissa.de/api/v1/repos/meissa/provs/releases/123/assets"
|
||||||
|
== sut.forgejo_release_asset_api_endpoint(123)
|
||||||
|
)
|
||||||
|
|
|
@ -14,6 +14,8 @@ def test_release_mixin(tmp_path):
|
||||||
copy_resource(Path("package.json"), tmp_path)
|
copy_resource(Path("package.json"), tmp_path)
|
||||||
project = Project(str_tmp_path, name="name")
|
project = Project(str_tmp_path, name="name")
|
||||||
|
|
||||||
|
os.environ["RELEASE_ARTIFACT_TOKEN"] = "ratoken"
|
||||||
|
|
||||||
sut = ReleaseMixin(
|
sut = ReleaseMixin(
|
||||||
project,
|
project,
|
||||||
devops_config(
|
devops_config(
|
||||||
|
|
Loading…
Reference in a new issue