Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
cedbc80a6e
|
|||
c8411ff6e8
|
|||
7bc47cf428
|
|||
5c4a169040
|
|||
0018c79b89
|
@@ -38,7 +38,7 @@ build_master_image:
|
||||
- apk add --no-cache git bash
|
||||
- scripts/get_version.sh generate
|
||||
- docker login -u ${CI_REGISTRY_USER} -p ${CI_REGISTRY_PASSWORD} ${CI_REGISTRY}
|
||||
- docker build --pull -t ${REGISTRY_IMAGE_LATEST} --build-arg CI_COMMIT_TAG=${CI_COMMIT_TAG} --build-arg CI_COMMIT_REF_NAME=${CI_COMMIT_REF_NAME} --build-arg CI_COMMIT_SHA=${CI_COMMIT_SHA} --build-arg CI_PROJECT_NAME=${CI_PROJECT_NAME} .
|
||||
- docker build --pull -t ${REGISTRY_IMAGE_LATEST} .
|
||||
- docker push ${REGISTRY_IMAGE_LATEST}
|
||||
|
||||
build_tag_image:
|
||||
@@ -52,5 +52,7 @@ build_tag_image:
|
||||
- apk add --no-cache git bash
|
||||
- scripts/get_version.sh generate
|
||||
- docker login -u ${CI_REGISTRY_USER} -p ${CI_REGISTRY_PASSWORD} ${CI_REGISTRY}
|
||||
- docker build -t ${REGISTRY_IMAGE_TAGGED} --build-arg CI_COMMIT_TAG=${CI_COMMIT_TAG} --build-arg CI_COMMIT_REF_NAME=${CI_COMMIT_REF_NAME} --build-arg CI_COMMIT_SHA=${CI_COMMIT_SHA} --build-arg CI_PROJECT_NAME=${CI_PROJECT_NAME} .
|
||||
- docker build -t ${REGISTRY_IMAGE_TAGGED} .
|
||||
- docker build -t ${REGISTRY_IMAGE_LATEST} .
|
||||
- docker push ${REGISTRY_IMAGE_TAGGED}
|
||||
- docker push ${REGISTRY_IMAGE_LATEST}
|
||||
|
16
CHANGELOG.md
16
CHANGELOG.md
@@ -6,6 +6,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.2.0]
|
||||
|
||||
### Added
|
||||
|
||||
- License information. We're on MIT.
|
||||
- Ability to show next issue/task creation timestamp using ``-show-next-creation-ts`` flag.
|
||||
|
||||
### Fixed
|
||||
|
||||
- "latest" Docker tag is now always latest, even when building images for tags.
|
||||
- Update github.com/robfig/cron to v3.
|
||||
|
||||
## [0.1.0] - 2021-09-29
|
||||
|
||||
- Initial release with ability to create issues on Gitlab (both hosted and self-hosted).
|
||||
|
||||
[Unreleased]: https://gitlab.pztrn.name/pztrn/periodicator/-/compare/v0.1.0...master
|
||||
[0.2.0]: https://gitlab.pztrn.name/pztrn/periodicator/-/compare/v0.1.0...v0.2.0
|
||||
[0.1.0]: https://gitlab.pztrn.name/pztrn/periodicator/-/releases/v0.1.0
|
||||
|
7
LICENSE
Normal file
7
LICENSE
Normal file
@@ -0,0 +1,7 @@
|
||||
Copyright 2021, Stanislav N. aka pztrn and Periodicator developers.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
5
Makefile
5
Makefile
@@ -12,6 +12,11 @@ run:
|
||||
GPT_CONFIG=$(CONFIG) ./periodicator
|
||||
rm periodicator
|
||||
|
||||
run-show-next-creation-ts:
|
||||
go build -ldflags $(LINKER_FLAGS) -o periodicator .
|
||||
GPT_CONFIG=$(CONFIG) ./periodicator -show-next-creation-ts
|
||||
rm periodicator
|
||||
|
||||
run-version:
|
||||
go build -ldflags $(LINKER_FLAGS) -o periodicator .
|
||||
GPT_CONFIG=$(CONFIG) ./periodicator -version
|
||||
|
3
go.mod
3
go.mod
@@ -3,12 +3,13 @@ module go.dev.pztrn.name/periodicator
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/robfig/cron v1.2.0
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/xanzy/go-gitlab v0.51.1
|
||||
gopkg.in/yaml.v2 v2.2.2
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/adhocore/gronx v0.2.5 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
|
6
go.sum
6
go.sum
@@ -33,6 +33,8 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/adhocore/gronx v0.2.5 h1:trVgNYPle0IcsGDt0XGz78zN8Soek0YrcQtQ+Cs+jko=
|
||||
github.com/adhocore/gronx v0.2.5/go.mod h1:7oUY1WAU8rEJWmAxXR2DN0JaO4gi9khSgKjiRypqteg=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
@@ -125,8 +127,8 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
|
||||
github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
|
@@ -4,7 +4,7 @@ import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/robfig/cron"
|
||||
"github.com/robfig/cron/v3"
|
||||
g "github.com/xanzy/go-gitlab"
|
||||
"go.dev.pztrn.name/periodicator/internal/gitlab"
|
||||
)
|
||||
@@ -39,6 +39,37 @@ func (b *BaseTask) checkIfOpenedTaskExists(issues []*g.Issue) bool {
|
||||
return foundAndNotClosed
|
||||
}
|
||||
|
||||
func (b *BaseTask) getIssues() ([]*g.Issue, error) {
|
||||
// nolint:wrapcheck
|
||||
return b.client.GetIssuesByTitle(b.projectID, b.title)
|
||||
}
|
||||
|
||||
func (b *BaseTask) getLastCreationTimestamp(issues []*g.Issue) time.Time {
|
||||
lastTaskCreationTS := b.executionStartTimestamp
|
||||
|
||||
for _, issue := range issues {
|
||||
if issue.ClosedAt != nil && issue.CreatedAt.After(lastTaskCreationTS) {
|
||||
lastTaskCreationTS = *issue.CreatedAt
|
||||
}
|
||||
}
|
||||
|
||||
return lastTaskCreationTS
|
||||
}
|
||||
|
||||
func (b *BaseTask) getNextCreationTimestamp(lastTaskCreationTS time.Time) time.Time {
|
||||
// Set up cron job parser.
|
||||
cp := cron.NewParser(cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow)
|
||||
|
||||
schedule, err := cp.Parse(b.cron)
|
||||
if err != nil {
|
||||
b.log("Failed to parse cron string: " + err.Error())
|
||||
|
||||
return lastTaskCreationTS
|
||||
}
|
||||
|
||||
return schedule.Next(lastTaskCreationTS)
|
||||
}
|
||||
|
||||
func (b *BaseTask) log(message string) {
|
||||
log.Println("Task '" + b.title + "': " + message)
|
||||
}
|
||||
@@ -46,7 +77,7 @@ func (b *BaseTask) log(message string) {
|
||||
// Run executes task procedure.
|
||||
func (b *BaseTask) Run() {
|
||||
// Get similar tasks.
|
||||
issues, err := b.client.GetIssuesByTitle(b.projectID, b.title)
|
||||
issues, err := b.getIssues()
|
||||
if err != nil {
|
||||
b.log("Error while getting issues from Gitlab: " + err.Error())
|
||||
|
||||
@@ -65,29 +96,16 @@ func (b *BaseTask) Run() {
|
||||
b.log("No still opened tasks found, checking if we should create new one...")
|
||||
|
||||
// Get latest task creation timestamp from Gitlab.
|
||||
lastTaskCreationTS := b.executionStartTimestamp
|
||||
|
||||
for _, issue := range issues {
|
||||
if issue.ClosedAt != nil && issue.CreatedAt.After(lastTaskCreationTS) {
|
||||
lastTaskCreationTS = *issue.CreatedAt
|
||||
}
|
||||
}
|
||||
lastTaskCreationTS := b.getLastCreationTimestamp(issues)
|
||||
|
||||
b.log("Last task creation timestamp: " + lastTaskCreationTS.String())
|
||||
|
||||
// Set up cron job parser.
|
||||
cp := cron.NewParser(cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow)
|
||||
|
||||
schedule, err := cp.Parse(b.cron)
|
||||
if err != nil {
|
||||
b.log("Failed to parse cron string: " + err.Error())
|
||||
|
||||
// Figure out next task creation and deadline timestamps.
|
||||
nextCreationTS := b.getNextCreationTimestamp(lastTaskCreationTS)
|
||||
if nextCreationTS.Equal(lastTaskCreationTS) {
|
||||
return
|
||||
}
|
||||
|
||||
// Figure out next task creation and deadline timestamps.
|
||||
nextCreationTS := schedule.Next(lastTaskCreationTS)
|
||||
|
||||
// Check if task should be created and create if so.
|
||||
if nextCreationTS.Before(time.Now()) {
|
||||
// Deadlines should be calculated until first one will appear AFTER today.
|
||||
|
@@ -4,6 +4,31 @@ import (
|
||||
"go.dev.pztrn.name/periodicator/internal/gitlab"
|
||||
)
|
||||
|
||||
// PrintCreationTSes prints tasks creation timestamps.
|
||||
func PrintCreationTSes(client *gitlab.Client, tasks []Config) {
|
||||
for _, task := range tasks {
|
||||
t := &BaseTask{
|
||||
client: client,
|
||||
projectID: task.ProjectID,
|
||||
title: task.Title,
|
||||
body: task.Body,
|
||||
tags: task.Tags,
|
||||
executionStartTimestamp: task.ExecutionStart.GetTimestamp(),
|
||||
cron: task.Cron,
|
||||
dueIn: task.DueIn,
|
||||
}
|
||||
|
||||
// Get similar tasks.
|
||||
// ToDo: refactor?
|
||||
issues, err := t.getIssues()
|
||||
if err != nil {
|
||||
panic("Error while getting issues from Gitlab: " + err.Error())
|
||||
}
|
||||
|
||||
t.log(t.getNextCreationTimestamp(t.getLastCreationTimestamp(issues)).String())
|
||||
}
|
||||
}
|
||||
|
||||
// Process processes passed tasks.
|
||||
func Process(client *gitlab.Client, tasks []Config) {
|
||||
for _, task := range tasks {
|
||||
|
10
main.go
10
main.go
@@ -11,7 +11,10 @@ import (
|
||||
"go.dev.pztrn.name/periodicator/internal/tasks"
|
||||
)
|
||||
|
||||
var showVersion = flag.Bool("version", false, "Show version information and exit")
|
||||
var (
|
||||
showNextCreationTS = flag.Bool("show-next-creation-ts", false, "Show tasks next creation timestamps")
|
||||
showVersion = flag.Bool("version", false, "Show version information and exit")
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
@@ -28,5 +31,10 @@ func main() {
|
||||
|
||||
c := gitlab.NewGitlabClient(&cfg.Gitlab)
|
||||
|
||||
if *showNextCreationTS {
|
||||
tasks.PrintCreationTSes(c, cfg.Tasks)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
tasks.Process(c, cfg.Tasks)
|
||||
}
|
||||
|
Reference in New Issue
Block a user