diff --git a/README.md b/README.md index 31de1e0..e5588f6 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,72 @@ -# jenkins-pipeline-shared-lib +# Jenkins pipelines -### Docs: +Репозиторий с пайплайнами Avroid -[Jenkins Shared Libraries](https://www.jenkins.io/doc/book/pipeline/shared-libraries/) +--- -[Pipeline CPS Method Mismatches](https://www.jenkins.io/doc/book/pipeline/cps-method-mismatches/) +Джобы в репозитории должны соответствовать той же ирархии каталогов что и +в **Jenkins**. -[Pipeline Best Practices](https://www.jenkins.io/doc/book/pipeline/pipeline-best-practices/) - -[Документирование классов в Java](https://java-online.ru/java-javadoc.xhtml) - -### Directory structure -The directory structure of a Shared Library repository is as follows: -``` -(root) -+- src # Groovy source files -| +- org -| +- foo -| +- Bar.groovy # for org.foo.Bar class -+- vars -| +- foo.groovy # for global 'foo' variable -| +- foo.txt # help for 'foo' variable -+- resources # resource files (external libraries only) -| +- org -| +- foo -| +- bar.json # static helper data for org.foo.Bar +```bash +├── jobs-dsl +│ ├── folders +│ │ └── Docker.groovy +│ ├── jobs +│ │ └── Docker +│ │ └── build_docker_image.groovy +│ └── views +│ └── devops.groovy +└── pipelines + └── Docker + └── build_docker_image.groovy ``` -The `src` directory should look like standard Java source directory structure. This directory is added to the classpath when executing Pipelines. +На изображении выше показана иерархия рабочих каталогов: +Каталог **jobs-dsl** содержит в себе 3 каталога folders, jobs, views. +В каждом из них содержиться описание сущностей jenkins. -The `vars` directory hosts script files that are exposed as a variable in Pipelines. The name of the file is the name of the variable in the Pipeline. So if you had a file called `vars/log.groovy` with a function like `def info(message)…` in it, you can access this function like `log.info "hello world"` in the Pipeline. You can put as many functions as you like inside this file. Read on below for more examples and options. +Далее идет каталог **pipelines**, в нем содержаться рабочии пайплайны. -The basename of each `.groovy` file should be a Groovy (~ Java) identifier, conventionally `camelCased`. The matching `.txt`, if present, can contain documentation, processed through the system’s configured markup formatter (so may really be HTML, Markdown, etc., though the `.txt` extension is required). This documentation will only be visible on the Global Variable Reference pages that are accessed from the navigation sidebar of Pipeline jobs that import the shared library. In addition, those jobs must run successfully once before the shared library documentation will be generated. +После создания джобы **pipeline**, необходимо добавить **job dsl** в директории **jobs-dsl** -The Groovy source files in these directories get the same “CPS transformation” as in Scripted Pipeline. +--- -A `resources` directory allows the `libraryResource` step to be used from an external library to load associated non-Groovy files. Currently this feature is not supported for internal libraries. +## jenkins-job-dsl + +Репозиторий для реализации методологии **IaC** для управления задачами в Jenkins +c использованием плагина [Job DSL](https://plugins.jenkins.io/job-dsl/) +[Ссылка](https://jenkins.devos.club/plugin/job-dsl/api-viewer/index.html) на локальную документацию. + +Джобы **dsl** должны называться через "_" пробелы и "-" запрещены, потому что у **job dsl** проблемы с парсингом. + +--- + +## Триггер джобы jobs-dsl + +Джоба [jobs-dsl](https://jenkins.avroid.tech/job/jobs-dsl/job/jobs-dsl/) вызывается через джобу [jobs-runner](https://jenkins.avroid.tech/job/gitea-events/job/jobs-runner/), которая + в свою очередь вызывается по **webhook** из **gitea**. + +--- + +## WebHook job + +В репозитории есть [джоба](pipelines/gitea-events/jobs-runner.groovy) которая запускает +другие джобы в зависомости от условий. + +Джоба имеет следующий мапинг + +```bash +Map projects = [ + // при такой конфигурации будет запускаться только одна джоба с проекта 'devops/jenkins-pipeline-shared-lib' + // из мастер ветки. Можно добавлять сюда еще и другие ветки. + 'devops/jenkins-pipeline-shared-lib': [ + 'branches': ['master'], + 'jobs': ['Docs/create_shared_libs_docs'] + ], + // при такой конфигурации будет запускаться только одна джоба с проекта 'antiq/tsnative' на любой ветке + 'antiq/tsnative': [ + 'branches': [], + 'jobs': ['gitea-events/add-git-tags-to-jira'] + ] +] +``` diff --git a/jobs-dsl/folders/bbl_waydroid.groovy b/jobs-dsl/folders/bbl_waydroid.groovy new file mode 100644 index 0000000..bdd9983 --- /dev/null +++ b/jobs-dsl/folders/bbl_waydroid.groovy @@ -0,0 +1,4 @@ +folder('BBL-Waydroid') { + displayName('BBL-Waydroid') + description("Job for build Waydroid") +} diff --git a/jobs-dsl/folders/gitea_events.groovy b/jobs-dsl/folders/gitea_events.groovy new file mode 100644 index 0000000..5733f0f --- /dev/null +++ b/jobs-dsl/folders/gitea_events.groovy @@ -0,0 +1,4 @@ +folder('gitea-events') { + displayName('gitea-events') + description("Jobs for events' Gitea") +} diff --git a/jobs-dsl/folders/jobs_dsl.groovy b/jobs-dsl/folders/jobs_dsl.groovy new file mode 100644 index 0000000..eeccf26 --- /dev/null +++ b/jobs-dsl/folders/jobs_dsl.groovy @@ -0,0 +1,4 @@ +folder('jobs-dsl') { + displayName('jobs-dsl') + description('Folder contains job for describing some DSL items such as Jenkins jobs, folders, views etc.') +} diff --git a/jobs-dsl/jobs/gitea_events/jobs_runner.groovy b/jobs-dsl/jobs/gitea_events/jobs_runner.groovy new file mode 100644 index 0000000..447f60a --- /dev/null +++ b/jobs-dsl/jobs/gitea_events/jobs_runner.groovy @@ -0,0 +1,36 @@ +pipelineJob('gitea-events/jobs-runner') { + definition { + cpsScm { + scm { + git { + remote { + url("${JENKINS_GIT_REPOSITORY_URL}/DevOps/jenkins-pipelines.git") + credentials("${JENKINS_GIT_CREDENTIALS_HTTP}") + } + branch('master') + } + } + scriptPath('pipelines/gitea-events/jobs-runner.groovy') + } + } + + properties { + pipelineTriggers { + triggers { + GenericTrigger { + allowSeveralTriggersPerBuild(false) + token('gitea-events/jobs-runner') + genericVariables { + genericVariable{ + expressionType('JSONPath') + regexpFilterText('') + regexpFilterExpression('') + key('jsonEvent') + value('\$') + } + } + } + } + } + } +} diff --git a/jobs-dsl/jobs/jobs_dsl/jobs_dsl.groovy b/jobs-dsl/jobs/jobs_dsl/jobs_dsl.groovy new file mode 100644 index 0000000..e3375d4 --- /dev/null +++ b/jobs-dsl/jobs/jobs_dsl/jobs_dsl.groovy @@ -0,0 +1,29 @@ +pipelineJob('jobs-dsl/jobs-dsl') { + definition { + cpsScm { + scm { + git { + remote { + url("${JENKINS_GIT_REPOSITORY_URL}/DevOps/jenkins-pipelines.git") + credentials("${JENKINS_GIT_CREDENTIALS_HTTP}") + } + branch('master') + } + } + scriptPath('pipelines/jobs-dsl/jobs-dsl.groovy') + } + } + + properties { + disableConcurrentBuilds() + } + + parameters { + string { name('PROJECT_NAME') + defaultValue('devops/jenkins-pipelines') + } + string { name('BRANCH_NAME') + defaultValue('master') + } + } +} diff --git a/jobs-dsl/views/devops.groovy b/jobs-dsl/views/devops.groovy new file mode 100644 index 0000000..637cf2c --- /dev/null +++ b/jobs-dsl/views/devops.groovy @@ -0,0 +1,15 @@ +listView('DevOps') { + description('DevOps service tasks') + recurse() + jobs { + regex(/^jobs-dsl$|^gitea*/) + } + columns { + status() + name() + lastSuccess() + lastFailure() + lastDuration() + buildButton() + } +} diff --git a/pipelines/gitea-events/jobs-runner.groovy b/pipelines/gitea-events/jobs-runner.groovy new file mode 100644 index 0000000..c855b43 --- /dev/null +++ b/pipelines/gitea-events/jobs-runner.groovy @@ -0,0 +1,85 @@ +@Library('shared-lib') _ + +properties([ + buildDiscarder(logRotator(artifactNumToKeepStr: '10', + numToKeepStr: '10')), + pipelineTriggers( + [GenericTrigger( + causeString: 'Generic Cause', + allowSeveralTriggersPerBuild: false, + genericVariables: [[key: 'jsonEvent', value: '$']], + token: env.JOB_NAME) + ] + ) +]) + +Map projects = [ + 'DevOps/jenkins-pipelines': [ + ['branches': [], + 'jobs': [[job: 'jobs-dsl/jobs-dsl']]], + ] +] + +String getBranch(Map fullJSON) { + String refHead = 'refs/heads/' + String refTags = 'refs/tags/' + + if (fullJSON.ref.contains(refHead)) { + return fullJSON.ref.split(refHead).last() + } + if (fullJSON.ref.contains(refTags)) { + return fullJSON.ref.split(refTags).last() + } + return fullJSON.ref +} + +podTemplate(yaml: ''' + apiVersion: v1 + kind: Pod + spec: + containers: + - name: linux + image: harbor.avroid.tech/tavro/base-build-image:1.0 + command: + - sleep + args: + - 99d + resources: + limits: + cpu: 300m + memory: 256Mi + requests: + cpu: 300m + memory: 256Mi + imagePullSecrets: + - name: harbor-registry-secret +''' +) { + node(POD_LABEL) { + stage('Run job') { + try { + Map fullJSON = readJSON text: jsonEvent + String branch = getBranch(fullJSON) + String projectName = fullJSON.repository.full_name + + println fullJSON //debug + currentBuild.description = "Repo: ${fullJSON.repository.full_name} ${branch}" + currentBuild.displayName = "#${env.BUILD_ID} Skip" + projects[projectName].each { project -> + currentBuild.displayName = "#${env.BUILD_ID} Run for branches" + project.jobs.each { job -> + build job: job.job, + parameters: [ + string(name: 'PROJECT_NAME', value: projectName), + string(name: 'BRANCH_NAME', value: branch), + string(name: 'DATA_JSON', value: fullJSON.toString()) + ], + wait: false + } + } + } catch (groovy.lang.MissingPropertyException e) { + println 'This job should run through gitea webhook:\n' + e + } + } + } +} diff --git a/pipelines/jobs-dsl/jobs-dsl.groovy b/pipelines/jobs-dsl/jobs-dsl.groovy new file mode 100644 index 0000000..ef7bd07 --- /dev/null +++ b/pipelines/jobs-dsl/jobs-dsl.groovy @@ -0,0 +1,62 @@ +@Library('shared-lib') _ + +import tech.avroid.scm.Git +import tech.avroid.ssh.Ssh + +properties([ + disableConcurrentBuilds(), + parameters([ + string(name: 'PROJECT_NAME', defaultValue: 'DevOps/jenkins-pipelines'), + string(name: 'BRANCH_NAME', defaultValue: 'master') + ]), +]) + +podTemplate(yaml: ''' + apiVersion: v1 + kind: Pod + spec: + containers: + - name: linux + image: harbor.avroid.tech/devops/base-build-image:1.0 + tty: true + resources: + limits: + cpu: 300m + memory: 256Mi + requests: + cpu: 300m + memory: 256Mi + imagePullPolicy: Always + imagePullSecrets: + - name: harbor-registry-secret +''' +) { + node(POD_LABEL) { + stage('Get repository') { + println(params.BRANCH_NAME) + withCredentials(bindings: [sshUserPrivateKey(credentialsId: env.JENKINS_GIT_CREDENTIALS_SSH, + keyFileVariable: 'SSH_KEY')]) { + def ssh = new Ssh(this, SSH_KEY, "id_ecdsa") + def git = new Git(this) + + ssh.configureSshClient() + git.clone([urlRepo: "$env.JENKINS_GIT_REPOSITORY_SSH_URL/$params.PROJECT_NAME", + branch: params.BRANCH_NAME]) + ssh.cleanupSshConfiguration() + } + } + + stage('Run job dsl') { + jobDsl( + targets: 'jobs-dsl/folders/*.groovy\n' + + 'jobs-dsl/jobs/**/*.groovy\n' + + 'jobs-dsl/views/**/*.groovy', + failOnSeedCollision: true, + lookupStrategy: 'JENKINS_ROOT', + removedJobAction: 'IGNORE', + removedViewAction: 'IGNORE', + removedConfigFilesAction: 'IGNORE' + ) + } + } +}