Skip to content

Commit 02465a4

Browse files
authored
Merge pull request #13 from tt-gf/new-master
Sync with upstream repository
2 parents e96ed42 + 2d2f2a7 commit 02465a4

File tree

235 files changed

+5098
-889
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

235 files changed

+5098
-889
lines changed

.github/workflows/cla.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: "CLA Assistant"
2+
on:
3+
# issue_comment triggers this action on each comment on issues and pull requests
4+
issue_comment:
5+
types: [created]
6+
pull_request_target:
7+
types: [opened,synchronize]
8+
9+
jobs:
10+
CLAssistant:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- uses: actions-ecosystem/action-regex-match@v2
14+
id: sign-or-recheck
15+
with:
16+
text: ${{ github.event.comment.body }}
17+
regex: '\s*(I have read the CLA Document and I hereby sign the CLA)|(recheckcla)\s*'
18+
19+
- name: "CLA Assistant"
20+
if: ${{ steps.sign-or-recheck.outputs.match != '' || github.event_name == 'pull_request_target' }}
21+
# Alpha Release
22+
uses: cla-assistant/[email protected]
23+
env:
24+
# Generated and maintained by github
25+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
26+
# JFrog organization secret
27+
PERSONAL_ACCESS_TOKEN : ${{ secrets.CLA_SIGN_TOKEN }}
28+
with:
29+
path-to-signatures: 'signed_clas.json'
30+
path-to-document: 'https://jfrog.com/cla/'
31+
remote-organization-name: 'jfrog'
32+
remote-repository-name: 'jfrog-signed-clas'
33+
# branch should not be protected
34+
branch: 'master'
35+
allowlist: bot*

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@
77
/.project
88
/.gradle
99
/.idea/
10-
*.iml
10+
*.iml
11+
12+
.DS_Store

DB/DBSyncNotification/DBSyncNotificationTest.groovy

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,36 @@ class DBSyncNotificationTest extends Specification {
55
def 'db sync notif test'() {
66
when:
77
def auth = "Basic ${'admin:password'.bytes.encodeBase64()}"
8-
def logs = 'http://localhost:8088/artifactory/api/systemlogs/logData?id=artifactory.log'
8+
def logs = 'http://localhost:8088/artifactory/api/systemlogs/downloadFile?id=console.log'
99
def sync = 'http://localhost:8081/artifactory/api/plugins/execute/syncNotification'
1010
def lastline = null, conn = null, lastlinum = -1, startlinum = -1
1111
// the test won't pass unless we wait for the HA nodes to connect
1212
def starttime = System.currentTimeMillis()
1313
while (true) {
1414
conn = new URL(logs).openConnection()
1515
conn.setRequestProperty('Authorization', auth)
16+
if (conn.responseCode != 200) {
17+
logs ='http://localhost:8088/artifactory/api/systemlogs/downloadFile?id=artifactory.log'
18+
conn = new URL(logs).openConnection()
19+
conn.setRequestProperty('Authorization', auth)
20+
}
1621
assert conn.responseCode == 200
17-
def oldlines = new JsonSlurper().parse(conn.inputStream).logContent.readLines()
22+
def oldlines = new InputStreamReader(conn.inputStream).readLines()
1823
conn.disconnect()
1924
lastline = oldlines.findAll { it ==~ /.*\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d.*/ }[-1]
20-
if (lastlinum > startlinum) break
2125
lastlinum = oldlines.findIndexOf { it == lastline }
22-
startlinum = oldlines.findIndexOf { it.contains('Artifactory successfully started') }
26+
startlinum = oldlines.findIndexOf { it.contains('All services started successfully') || it.contains('Artifactory successfully started') }
27+
if (lastlinum > startlinum) break
28+
if (System.currentTimeMillis() > starttime + 120000) {
29+
if (startlinum > -1) {
30+
break
31+
} else {
32+
throw new Exception("Failed to get last log line after Artifactory startup")
33+
}
34+
}
2335
System.sleep(5000)
24-
assert System.currentTimeMillis() <= starttime + 120000
2536
}
37+
2638
conn = new URL(sync).openConnection()
2739
conn.requestMethod = 'POST'
2840
conn.setRequestProperty('Authorization', auth)
@@ -32,7 +44,7 @@ class DBSyncNotificationTest extends Specification {
3244
conn = new URL(logs).openConnection()
3345
conn.setRequestProperty('Authorization', auth)
3446
assert conn.responseCode == 200
35-
def lines = new JsonSlurper().parse(conn.inputStream).logContent.readLines()
47+
def lines = new InputStreamReader(conn.inputStream).readLines()
3648
conn.disconnect()
3749
def idx = lines.findIndexOf { it == lastline }
3850
def newlines = lines[idx + 1 .. -1]

archive/archiveOldArtifacts/ArchiveOldArtifactsTest.groovy

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ import spock.lang.Specification
22

33
import org.jfrog.artifactory.client.model.repository.settings.impl.MavenRepositorySettingsImpl
44

5-
import static org.jfrog.artifactory.client.ArtifactoryClient.create
5+
import org.jfrog.artifactory.client.ArtifactoryClientBuilder
66

77
class ArchiveOldArtifactsTest extends Specification {
88
def 'archive old artifacts plugin test'() {
99
setup:
1010
def baseurl = 'http://localhost:8088/artifactory'
11-
def artifactory = create(baseurl, 'admin', 'password')
11+
def artifactory = ArtifactoryClientBuilder.create().setUrl(baseurl)
12+
.setUsername('admin').setPassword('password').build()
1213

1314
def builder = artifactory.repositories().builders()
1415
def local1 = builder.localRepositoryBuilder().key('maven-local')

archive/archiveOldArtifacts/archiveOldArtifacts.groovy

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import org.artifactory.exception.CancelException
1818
import org.artifactory.fs.StatsInfo
19+
import org.artifactory.model.xstream.fs.StatsImpl
1920
import org.artifactory.md.Properties
2021
import org.artifactory.repo.RepoPath
2122
import org.artifactory.repo.RepoPathFactory
@@ -376,6 +377,13 @@ boolean checkArchiveTimingPolicies(
376377
if (lastDownloadedDays != 0) {
377378
// Get the StatsInfo on the item
378379
def statsInfo = (StatsInfo) repositories.getStats(artifact)
380+
381+
// If artifact is never downloaded
382+
if (statsInfo == null){
383+
statsInfo = new StatsImpl()
384+
statsInfo.lastDownloaded = itemInfo.getCreated()
385+
}
386+
379387
log.debug('Artifact {} stats info: {}', artifact, statsInfo)
380388

381389
// Get the last downloaded date

backup/backupFolders/BackupFoldersTest.groovy

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import org.jfrog.artifactory.client.model.repository.settings.impl.GenericReposi
33
import org.jfrog.lilypad.Control
44
import spock.lang.Specification
55

6-
import static org.jfrog.artifactory.client.ArtifactoryClient.create
6+
import org.jfrog.artifactory.client.ArtifactoryClientBuilder
77

88
/*
99
* Copyright (C) 2017 JFrog Ltd.
@@ -38,7 +38,7 @@ class BackupFoldersTest extends Specification {
3838
def backupFolder = "/tmp/backupfoldertest/${System.currentTimeMillis()}"
3939

4040
setup:
41-
def artifactory = create(baseurl, user, password)
41+
def artifactory = ArtifactoryClientBuilder.create().setUrl(baseurl).setUsername(user).setPassword(password).build()
4242
// Create temporary folder to receive backup
4343
Control.createFolder(basePort, backupFolder)
4444
// Create repository
@@ -71,7 +71,7 @@ class BackupFoldersTest extends Specification {
7171
def backupFolder = "/tmp/backupfoldertest/${System.currentTimeMillis()}"
7272

7373
setup:
74-
def artifactory = create(baseurl, user, password)
74+
def artifactory = ArtifactoryClientBuilder.create().setUrl(baseurl).setUsername(user).setPassword(password).build()
7575
// Create temporary folder to receive backup
7676
Control.createFolder(basePort, backupFolder)
7777
// Create repository

backup/backupFolders/backupFolders.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ jobs {
6161
backUpFolder(cron: "0 0 1 1/1 * ? *") {
6262
try {
6363
//getting the information from the properties file
64-
def etcdir = ctx.artifactoryHome.haAwareEtcDir
64+
def etcdir = ctx.artifactoryHome.etcDir
6565
def configFile = new ConfigSlurper().parse(new File(etcdir, "plugins/backupFolders.properties").toURL())
6666
startBackup(configFile.destinationFolder as String, configFile.pathToFolder as String)
6767

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import com.sun.net.httpserver.HttpExchange
2+
import com.sun.net.httpserver.HttpHandler
3+
import com.sun.net.httpserver.HttpServer
4+
import groovy.json.JsonBuilder
5+
import groovy.json.JsonSlurper
6+
import spock.lang.Specification
7+
import org.jfrog.artifactory.client.ArtifactoryClientBuilder
8+
9+
class AtlassianTest extends Specification {
10+
def 'atlassian plugin test'() {
11+
setup:
12+
def baseurl = 'http://localhost:8088/artifactory'
13+
def auth = "Basic ${("admin:password").bytes.encodeBase64().toString()}"
14+
def artifactory = ArtifactoryClientBuilder.create().setUrl(baseurl)
15+
.setUsername('admin').setPassword('password').build()
16+
def mockServer = new MockServer(port: 7990)
17+
mockServer.start()
18+
19+
when:
20+
def conn = new URL(baseurl + '/api/build').openConnection()
21+
conn.setDoOutput(true)
22+
conn.setRequestMethod('PUT')
23+
conn.setRequestProperty('Content-Type', 'application/json')
24+
conn.setRequestProperty('Authorization', auth)
25+
def jsonPayload = new JsonBuilder([version: '1.0.1', name: 'atlassian-test-build', number: '1', type: 'GENERIC', agent: [name: "Jenkins", version: "1.0.0"], buildAgent: [name: "Maven", version: "1.0.0"], started: '2020-03-05T21:47:17.229+0000', durationMillis: 2292103, vcsUrl: "http://localhost:7990/foo/bar", vcsRevision: 'b2869957593381150d3d3fb854674396b69e51c7'])
26+
conn.outputStream.withCloseable { output ->
27+
output.write(jsonPayload.toString().bytes)
28+
}
29+
assert conn.responseCode == 204
30+
mockServer.waitForMessage()
31+
32+
then:
33+
mockServer.messageReceived == true
34+
mockServer.validMessageReceived == true
35+
36+
cleanup:
37+
mockServer?.stop()
38+
}
39+
40+
class MockServer {
41+
HttpServer server
42+
int port = 7990
43+
boolean messageReceived
44+
boolean validMessageReceived
45+
46+
def start() {
47+
InetSocketAddress address = new InetSocketAddress(port);
48+
server = HttpServer.create(address, 0);
49+
HttpHandler requestValidatorHandler = new HttpHandler() {
50+
@Override
51+
void handle(HttpExchange exchange) throws IOException {
52+
println "Mock server receive a new request"
53+
messageReceived = true
54+
def content = exchange.requestBody.text
55+
println "Message content: $content"
56+
validMessageReceived = isMessageContentValid(content)
57+
exchange.sendResponseHeaders(HttpURLConnection.HTTP_OK, 0)
58+
exchange.close()
59+
}
60+
61+
boolean isMessageContentValid(content) {
62+
def jsonContent = new JsonSlurper().parseText(content)
63+
assert jsonContent.server == 'http://localhost:8088/artifactory'
64+
assert jsonContent.state == 'SUCCESSFUL'
65+
assert jsonContent.key == 'atlassian-test-build'
66+
assert jsonContent.resultKey == 'atlassian-test-build:1'
67+
assert jsonContent.name == 'atlassian-test-build'
68+
assert jsonContent.url == "http://localhost:8088/ui/builds/atlassian-test-build/1/1583444837229"
69+
assert jsonContent.description == "atlassian-test-build build 1 was successfully published to Artifactory at 2020-03-05T21:47:17.229+0000 by Jenkins/1.0.0 using tool Maven/1.0.0"
70+
assert jsonContent.duration == 2292.103
71+
return true
72+
}
73+
}
74+
server.createContext("/rest/build-status/1.0/commits/b2869957593381150d3d3fb854674396b69e51c7", requestValidatorHandler)
75+
server.start()
76+
println "Mock server started"
77+
}
78+
79+
def stop() {
80+
server?.stop(0)
81+
println "Mock server stopped"
82+
}
83+
84+
def waitForMessage() {
85+
println "Waiting up to 2 minutes for notification receiving..."
86+
def initialTime = System.currentTimeMillis()
87+
while (System.currentTimeMillis() - initialTime < 120000) {
88+
sleep(5000)
89+
if (this.messageReceived) {
90+
sleep (5000)
91+
break
92+
}
93+
}
94+
}
95+
}
96+
}

build/atlassian/README.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Artifactory Atlassian Integration User Plugin
2+
3+
This plugin provides some integration with Atlassian Bitbucket Server. It allows
4+
commits in Bitbucket to provide information and links to associated builds in
5+
Artifactory.
6+
7+
## Usage
8+
9+
When a build is deployed in Artifactory, the plugin automatically sends the
10+
build information to the configured Bitbucket Server. Bitbucket then adds the
11+
build to the appropriate commit, accessible through the "Builds" link in the top
12+
right:
13+
14+
![commit](imgs/commit.png)
15+
16+
![links](imgs/links.png)
17+
18+
## Configuration
19+
20+
The `atlassian.json` file should be modified to allow the plugin to access the
21+
Bitbucket server:
22+
23+
``` json
24+
{
25+
"bitbucketUrl": "http://localhost:7990",
26+
"username": "admin",
27+
"password": "password"
28+
}
29+
```
30+
31+
The URL, username, and password should be changed to the correct location and
32+
login credentials.

build/atlassian/atlassian.groovy

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import groovy.json.JsonBuilder
2+
import groovy.json.JsonSlurper
3+
import java.text.SimpleDateFormat
4+
import java.text.ParseException
5+
6+
import org.artifactory.build.DetailedBuildRunImpl
7+
import org.artifactory.request.RequestThreadLocal
8+
import org.artifactory.util.HttpUtils
9+
10+
build {
11+
afterSave { buildRun ->
12+
def cfgfile = new File(ctx.artifactoryHome.etcDir, 'plugins/atlassian.json')
13+
def cfg = new JsonSlurper().parse(cfgfile)
14+
def arturl = ctx.centralConfig.descriptor.urlBase
15+
if (!arturl) {
16+
def req = RequestThreadLocal.context.get().requestThreadLocal
17+
arturl = HttpUtils.getServletContextUrl(req.request)
18+
}
19+
arturl = arturl - ~'/artifactory/?$'
20+
def build = (buildRun as DetailedBuildRunImpl).build
21+
if (!build.vcsUrl?.startsWith(cfg.bitbucketUrl)) {
22+
return
23+
}
24+
// Process the started date
25+
def fmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
26+
def date
27+
try {
28+
date = fmt.parse(build.started)
29+
} catch (ParseException ex) {
30+
log.warn("Unable to parse build started timestamp $build.started: $ex")
31+
return
32+
}
33+
// Generate the build url
34+
def buildurl = arturl + "/ui/builds/$build.name/$build.number/$date.time"
35+
// Build the request body
36+
def body = [:]
37+
body.server = arturl + '/artifactory'
38+
body.state = 'SUCCESSFUL'
39+
body.key = build.name
40+
body.resultKey = build.name + ':' + build.number
41+
body.name = build.name
42+
body.url = buildurl
43+
body.description = "$build.name build $build.number was successfully published to Artifactory at $build.started by ${build.agent?.name}/${build.agent?.version} using tool ${build.buildAgent?.name}/${build.buildAgent?.version}"
44+
body.duration = build.durationMillis/1000
45+
def bodytext = new JsonBuilder(body).toPrettyString()
46+
log.debug("Build triggered, payload follows:\n$bodytext")
47+
def url = "$cfg.bitbucketUrl/rest/build-status/1.0/commits/$build.vcsRevision"
48+
def auth = "Basic ${"$cfg.username:$cfg.password".bytes.encodeBase64().toString()}"
49+
def conn = new URL(url).openConnection()
50+
conn.doOutput = true
51+
conn.requestMethod = 'POST'
52+
conn.setRequestProperty('Content-Type', 'application/json')
53+
conn.setRequestProperty('Authorization', auth)
54+
conn.outputStream << bodytext
55+
if (conn.responseCode == 204) {
56+
log.debug("Success!")
57+
} else {
58+
log.debug("Failure. $conn.responseCode")
59+
}
60+
}
61+
}

0 commit comments

Comments
 (0)