Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
root = true

[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
max_line_length = 80


[*.{js, html}]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true


4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@ work/
.idea
*.iml
/nbproject/
build
node_modules
src/main/webapp/page/
src/main/react/node
4 changes: 3 additions & 1 deletion Jenkinsfile
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
buildPlugin()
buildPlugin(configurations: [
[ platform: "linux", jdk: "8", jenkins: null ],
])
119 changes: 104 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
<style>
.img {
margin:30px 0;
box-shadow:1px 1px 1px 1px #cccc
}

.img-big{
width:70%;
margin-left:15%;
}

.img-small{
width:40%;
margin-left:30%;
}
</style>

# Working Hours Jenkins Plugin

[![Join the chat at https://gitter.im/jenkinsci/working-hours-plugin](https://badges.gitter.im/jenkinsci/working-hours-plugin.svg)](https://gitter.im/jenkinsci/working-hours-plugin?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
Expand All @@ -9,7 +26,8 @@ configured allowable build times. If a build is scheduled during non-working hou
then it is kept in the build queue until the next allowable time.

Jobs opt in via the `enforceBuildSchedule` job parameter, which is provided by this
plugin.
plugin. It can optionally take in a `branches` parameter to limit it's usage to only those branches.
This only works in MultiBranchPipelines.

## Usage

Expand Down Expand Up @@ -41,6 +59,16 @@ pipeline {
}
```

Sample job with branches parameter (works in both declarative and scripted):
```
node {
properties([enforceBuildSchedule(branches: ['dev', 'qa', 'prod')])
stage('Do some stuff') {
echo 'this can wait til morning'
}
}
```

When the job runs outside of configured hours, you'll be able to see a tooltip from the job in the queue:

![Queued job](images/queued-job.png "Tooltip for blocked job")
Expand All @@ -63,28 +91,89 @@ and the build page:

## Configuration

In order to use the working hours plugin, you must set up a schedule in your Jenkins
system configuration page. You can configure both daily working hours and specific dates (such
as holidays). The following configuration will cause jobs with `enforceBuildSchedule` to queue if ran outside of Monday - Friday 8:00 AM to 6:00 PM.
In order to use the working hours plugin, you must set up a schedule in the plugin's configuration page, you can access by 2 methods:
- Access by url, typically the url is `/working-hours/`, sometimes your url may have a prefix, then the url may be like `/jenkins/working-hours/`

- Access inside the system management page ( url: `/manage` ), which can be accessed by the button on the sidebar

<img class='img img-small' src="images/sidebar-entry.png">

Then Working Hours Plugin's entry is down below at

<img class='img img-big' src="images/manage-entry.png">

You can configure both daily working hours and specific dates (such
as holidays). The following configuration will cause jobs with `enforceBuildSchedule` to queue if ran outside of Monday/Tuesday/Friday's 8:00 AM to 6:00 PM.

<img class='img img-big' src="images/working-hours-config.png">

### Configure timezone
You can set the base timezone of your time ranges and excluded dates, they share the same config, which can be found on the right top, and would automatically save when changed.

<img class='img img-big' src="images/timezone-config.png">

*Note:* all times are local to your Jenkins master.
### Time ranges
This section contains the times when guarded steps are allowed for each day. You can click the seven buttons to add the day you want to allow, once it's added, you can set time range for it using the slide bar, for precision under 5 minutes, you can manually input the time like `12:06`.

![Configuration options](images/working-hours-config.png "Configuration options")
**Note** All time ranges are based on the timezone setting.

### Allowable build times
This section contains the times when guarded steps are allowed for each day. Times can be entered in one of the following formats:
- 24 hour eg `09:00`, `18:00`
- Abbreviated 24 hour eg `0900`, `1800`
- 12 hour eg `9:00 AM`, `6:00 PM`
<img class='img img-big' src="images/time-range.png">

**Note** All times are local to your Jenkins master.

If the `enforceBuildStep` runs at a time that is not between a configured time range
for the day it's running, the job will be aborted. Please note that if no allowable
time ranges are configured for a day, `enforceBuildStep` will abort the job.

### Excluded dates
This section contains explicit days (such as holidays) to abort the `enforceBuildStep`. This takes precedence over a`Allowable build times`, so jobs
will always be queued on an excluded date. Here's an example of some holidays configured in the Working Days calendar:
This section contains days to abort the `enforceBuildStep`. This takes precedence over a`Allowable build times`, so jobs
will always be queued on an excluded date.
<img class='img img-big' src="images/excluded-date.png">


The `Start Date` indicates the rule that you want to define a date, you can set three types for a `Start Date`
- Static

Default, a `Start Date` is a static date, you can click on the date and choose the target date with the datepicker.
<img class='img img-small' src="images/static-date.png">

- Dynamic

If the dynamic option is checked, you can select a date on a dynamic base like `the second Sunday of July`.
<img class='img img-big' src="images/dynamic-date.png">

- Holiday

You can open a dialog to choose holiday presets by clicking the button
<img class='img img-big' src="images/preset-select-entry.png">
Then in the dialog, you can first select a region, and then select a regional holiday, finally click apply to apply this holiday.
<img class='img img-big' src="images/presets.png">

### There are also repeat configs to help you setting up a repeating rule
- Repeat period

You can set this to week/month/year, then your date would repeat weekly/monthly/yearly
- Repeat interval

Means how many periods are there between two occurrence. Default is 1, means your date will repeat each week/month/year.
- Repeat count

Means how many times your date would repeat, doesn't include the occurrences skipped by the `repeat interval`.


**Note** These three repeat configs are only available for `Static/Dynamic` dates, `holidays` would default repeat each year and with no max repeat count.


- End Date

The `End Date` is a static date, `any` dates would stop repeat after this date, you can set an end date by not checking the `No End` checkbox.

**Note** The repeat would also stop if it meets the repeat count.

<img class='img img-small' src="images/end-date.png">

- Repeat

As repeat is default enabled, you can still disable a repeat by not checking the repeat checkbox


![Excluded days](images/excluded-days.png "Excluded days")
<img class='img img-small' src="images/no-repeat.png">
Binary file added images/data-acquisition.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/dynamic-date.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/end-date.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/excluded-date.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/manage-entry.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/no-repeat.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/preset-select-entry.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/presets.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/sidebar-entry.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/source-code.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/static-date.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/time-range.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/timezone-config.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified images/working-hours-config.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
75 changes: 71 additions & 4 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>plugin</artifactId>
<version>3.6</version>
<relativePath />
<relativePath/>
</parent>
<artifactId>working-hours</artifactId>
<version>1.1-SNAPSHOT</version>
<packaging>hpi</packaging>
<properties>
<jenkins.version>2.73.3</jenkins.version>
<java.level>8</java.level>
<node.version>10.13.0</node.version>
<npm.version>6.8.0</npm.version>
<javadoc.exec.goal>javadoc-no-fork</javadoc.exec.goal> <!-- stop initialize phase plugins executing twice -->
</properties>
<name>Working Hours Plugin</name>
<description>Queues builds that ran after certain working hours</description>
Expand All @@ -23,6 +27,7 @@
<url>http://opensource.org/licenses/MIT</url>
</license>
</licenses>

<dependencies>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
Expand Down Expand Up @@ -131,9 +136,71 @@
<scope>test</scope>
</dependency>

<dependency>
<groupId>de.jollyday</groupId>
<artifactId>jollyday</artifactId>
<version>0.4.9</version>
</dependency>

<dependency>
<groupId>com.github.heqiao2010</groupId>
<artifactId>lunar</artifactId>
<version>1.0</version>
</dependency>

</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<source>8</source>
</configuration>
</plugin>
<plugin>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
<!-- NB! Set <version> to the latest released version of frontend-maven-plugin, like in README.md -->
<version>1.7.5</version>
<configuration>
<workingDirectory>src/main/react</workingDirectory>
</configuration>
<executions>
<execution>
<id>install node and npm</id>
<goals>
<goal>install-node-and-npm</goal>
</goals>
<configuration>
<!-- See https://nodejs.org/en/download/ for latest node and npm (lts) versions -->
<nodeVersion>v${node.version}</nodeVersion>
<npmVersion>${npm.version}</npmVersion>
<nodeDownloadRoot>https://repo.jenkins-ci.org/nodejs-dist/</nodeDownloadRoot>
<npmDownloadRoot>https://repo.jenkins-ci.org/npm-dist/</npmDownloadRoot>
</configuration>
</execution>
<execution>
<id>npm install</id>
<goals>
<goal>npm</goal>
</goals>
<configuration>
<arguments>install</arguments>
</configuration>
</execution>
<execution>
<id>npm run build:copy</id>
<goals>
<goal>npm</goal>
</goals>
<configuration>
<arguments>run build:copy</arguments>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

Expand All @@ -149,8 +216,8 @@
<connection>scm:git:git://github.com/jenkinsci/${project.artifactId}-plugin.git</connection>
<developerConnection>scm:git:[email protected]:jenkinsci/${project.artifactId}-plugin.git</developerConnection>
<url>https://github.com/jenkinsci/${project.artifactId}-plugin</url>
<tag>HEAD</tag>
</scm>
<tag>HEAD</tag>
</scm>
<repositories>
<repository>
<id>repo.jenkins-ci.org</id>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.jenkinsci.plugins.workinghours;

import net.sf.json.JSONObject;

public class ValidationResult {
private final boolean valid;
private final String errorMessage;
private final String fieldName;

public ValidationResult(boolean valid, String field, String errorMessage) {
this.valid = valid;
this.fieldName = field;
this.errorMessage = errorMessage;
}

public ValidationResult(boolean valid) {
this.valid = valid;
this.fieldName = "";
this.errorMessage = "";
}

public static ValidationResult getSuccessValidation() {
return new ValidationResult(true, "", "");
}

public JSONObject toJSON() {
return new JSONObject().accumulate("valid", this.valid)
.accumulate("field", this.fieldName)
.accumulate("errorMessage", this.errorMessage);
}

public String toErrorMessage() {
return this.fieldName + " " + this.errorMessage;
}

public boolean isValid(){
return this.valid;
}
}
Loading