mirror of
https://github.com/jlengrand/github-api.git
synced 2026-03-11 08:21:23 +00:00
Compare commits
350 Commits
github-api
...
github-api
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b1ff0a4453 | ||
|
|
6564648230 | ||
|
|
48cadbc814 | ||
|
|
fbfba70714 | ||
|
|
30a6cc504e | ||
|
|
54d8fe93a8 | ||
|
|
4abf33acdb | ||
|
|
c00d562b48 | ||
|
|
fad4753f0f | ||
|
|
c24df1d022 | ||
|
|
823465590e | ||
|
|
804db70049 | ||
|
|
1782e5a483 | ||
|
|
b6283a0493 | ||
|
|
0bb18ee7c5 | ||
|
|
1a77dd270f | ||
|
|
1c6ab19d59 | ||
|
|
ef4e64dcdd | ||
|
|
946b4e963b | ||
|
|
0d7867daf6 | ||
|
|
3044b5437b | ||
|
|
4a2886531d | ||
|
|
c5e2d8b2ae | ||
|
|
3041431468 | ||
|
|
63b9188dad | ||
|
|
2436ed0431 | ||
|
|
0295ad82fa | ||
|
|
c197dc6b7b | ||
|
|
a79971e406 | ||
|
|
1a56f9d093 | ||
|
|
35852055e8 | ||
|
|
196ee25452 | ||
|
|
7d8335423d | ||
|
|
eac4990eac | ||
|
|
0f17812732 | ||
|
|
3bafb965c5 | ||
|
|
0e02444c07 | ||
|
|
4dcc479d48 | ||
|
|
a564c97763 | ||
|
|
80e17109de | ||
|
|
c395b9d6b3 | ||
|
|
e34d33f1cd | ||
|
|
1c920dee06 | ||
|
|
955d2e0a07 | ||
|
|
d0912009dd | ||
|
|
e68950e619 | ||
|
|
93c026b7af | ||
|
|
5254839ff7 | ||
|
|
6ca0d83b70 | ||
|
|
8ed832a303 | ||
|
|
dbf6d3bf37 | ||
|
|
081a454ec8 | ||
|
|
543b643fdb | ||
|
|
d02f194668 | ||
|
|
9c8c00b77c | ||
|
|
a23de4707b | ||
|
|
301303bd90 | ||
|
|
4689b8f885 | ||
|
|
c4de682493 | ||
|
|
b23934a5a1 | ||
|
|
f2eecc3cc5 | ||
|
|
f5310965dc | ||
|
|
47ffff3407 | ||
|
|
f2a70a46ad | ||
|
|
acd5c6baa6 | ||
|
|
06d02059cb | ||
|
|
603288c361 | ||
|
|
09ee3168f9 | ||
|
|
1559d063c7 | ||
|
|
cfdcb182a4 | ||
|
|
d526b13d7d | ||
|
|
fffe31220e | ||
|
|
ce17396ea6 | ||
|
|
d18e81dc74 | ||
|
|
6ae5acba5d | ||
|
|
0a1c803f69 | ||
|
|
fa0865b208 | ||
|
|
886887913c | ||
|
|
5c64fec032 | ||
|
|
892f60ea16 | ||
|
|
f28f966040 | ||
|
|
0e9cc90d31 | ||
|
|
72dc5c5d18 | ||
|
|
02e02d39b0 | ||
|
|
e629a23bd4 | ||
|
|
f6e8a2c7c6 | ||
|
|
76bea5174f | ||
|
|
2be27d1a41 | ||
|
|
cd1454ac03 | ||
|
|
b550910f4c | ||
|
|
d13e490be2 | ||
|
|
3d451526ef | ||
|
|
bd38897d48 | ||
|
|
63ccbaf064 | ||
|
|
2beb806b8a | ||
|
|
552ba6693e | ||
|
|
2452add4d7 | ||
|
|
0dc931ceed | ||
|
|
1dfedc6a58 | ||
|
|
e33046a624 | ||
|
|
002b3f03da | ||
|
|
fd1817d111 | ||
|
|
7526b46f9d | ||
|
|
169fd18a54 | ||
|
|
0708812456 | ||
|
|
7d86070ac8 | ||
|
|
713b85f9de | ||
|
|
4fef5bb1fc | ||
|
|
f3eadcddb6 | ||
|
|
237171727d | ||
|
|
31212d33ae | ||
|
|
8af66133d2 | ||
|
|
9578e027b1 | ||
|
|
2c75b42b4e | ||
|
|
7854b30a76 | ||
|
|
cf2571858c | ||
|
|
092815747a | ||
|
|
649d7ed87f | ||
|
|
684560ef67 | ||
|
|
adf054ba5d | ||
|
|
dcdfee67cd | ||
|
|
9d7209ec62 | ||
|
|
b97e8a2c38 | ||
|
|
8bd3f391da | ||
|
|
5d0dbf6e2f | ||
|
|
38f3595552 | ||
|
|
b72e7fa2ee | ||
|
|
659b32f5ec | ||
|
|
d0c326bbf5 | ||
|
|
4a5aceb1f9 | ||
|
|
884248930e | ||
|
|
530d524366 | ||
|
|
5957da3d6d | ||
|
|
6efe428f57 | ||
|
|
25b9a2ce33 | ||
|
|
0ce78016cc | ||
|
|
696dd90b23 | ||
|
|
e66a72387e | ||
|
|
874ce23dd7 | ||
|
|
7479cac9a7 | ||
|
|
064ce1b0bc | ||
|
|
941573af49 | ||
|
|
2f9ff32176 | ||
|
|
b84d5a7c39 | ||
|
|
bd19f23b3f | ||
|
|
ee047ea9b5 | ||
|
|
601f18016a | ||
|
|
93abb0ed36 | ||
|
|
6453e585a9 | ||
|
|
3a1ed5a5b7 | ||
|
|
c5b45523d6 | ||
|
|
bf082f2a46 | ||
|
|
672febd88b | ||
|
|
927843ea83 | ||
|
|
8fac7d317e | ||
|
|
626574ae36 | ||
|
|
8c9eb3393b | ||
|
|
0c4728f46a | ||
|
|
837526ce5d | ||
|
|
afcfa906b8 | ||
|
|
8b3f50d4d3 | ||
|
|
9022455d85 | ||
|
|
8e20f4d9f5 | ||
|
|
7c8a7ff26e | ||
|
|
064d6944f3 | ||
|
|
b8b3cf9c80 | ||
|
|
18e7138812 | ||
|
|
bfb3b94478 | ||
|
|
6167d196d9 | ||
|
|
43ed7c7ac7 | ||
|
|
fc98e72569 | ||
|
|
258acf79f6 | ||
|
|
b509076d6f | ||
|
|
f57ea4c4e9 | ||
|
|
578fe085ce | ||
|
|
2553a79b02 | ||
|
|
4770316898 | ||
|
|
99f192d33c | ||
|
|
fc3bac0e77 | ||
|
|
ad2990b1b6 | ||
|
|
fab848a0d3 | ||
|
|
4a2244e661 | ||
|
|
bab5399327 | ||
|
|
52705ac695 | ||
|
|
73d2e1db5c | ||
|
|
83aa9d04ef | ||
|
|
97652c6803 | ||
|
|
f40daf8488 | ||
|
|
3e6a5bc718 | ||
|
|
78ffe5a759 | ||
|
|
9abfdc805b | ||
|
|
9e47a2b8c6 | ||
|
|
feba6ed8b6 | ||
|
|
acab40b704 | ||
|
|
435272065f | ||
|
|
c5c04672fc | ||
|
|
5eef764cba | ||
|
|
2682e0a1e2 | ||
|
|
a68d16d5de | ||
|
|
304ab10cf9 | ||
|
|
dc46341432 | ||
|
|
99aea9296e | ||
|
|
b0693037f3 | ||
|
|
c19cfd98d1 | ||
|
|
cdc0e2ad6b | ||
|
|
6606b5c7d1 | ||
|
|
551dbf2a06 | ||
|
|
d734237788 | ||
|
|
47e2a5aea1 | ||
|
|
57cdc308e8 | ||
|
|
8919c5f8c7 | ||
|
|
b8f00bc699 | ||
|
|
042038f480 | ||
|
|
fb03e749bd | ||
|
|
e522239832 | ||
|
|
ae69324196 | ||
|
|
5194c2d9bc | ||
|
|
daf5c5eb98 | ||
|
|
a7b4c97020 | ||
|
|
420d5d06f3 | ||
|
|
a7cd052b7c | ||
|
|
6e1b943823 | ||
|
|
8a3559ada5 | ||
|
|
ea3cbd4c71 | ||
|
|
34a1f9d6e4 | ||
|
|
629bd510c1 | ||
|
|
40937a5cc6 | ||
|
|
8509957102 | ||
|
|
b0aea0c575 | ||
|
|
1f7f646bec | ||
|
|
a59ee6a82d | ||
|
|
1fefc77582 | ||
|
|
199eee4e25 | ||
|
|
854df5321b | ||
|
|
bd509070ac | ||
|
|
a8c7c97d06 | ||
|
|
6d86cfb4f6 | ||
|
|
fb3e956502 | ||
|
|
9b0dbe6f34 | ||
|
|
c10c7237a7 | ||
|
|
36612fe97f | ||
|
|
18e2056a10 | ||
|
|
8c8f1451d4 | ||
|
|
be67f1d9e2 | ||
|
|
90bc250269 | ||
|
|
1bd178654f | ||
|
|
f22bf160f9 | ||
|
|
4261c42949 | ||
|
|
40cfb85a8e | ||
|
|
f08299b134 | ||
|
|
a04ab45abc | ||
|
|
0647df2d2b | ||
|
|
d4cc3af1e9 | ||
|
|
936ab499ce | ||
|
|
453f475b4e | ||
|
|
bda3855b86 | ||
|
|
772a6c112b | ||
|
|
9b4134cada | ||
|
|
ed9f54006d | ||
|
|
3b1f176544 | ||
|
|
d2732bcf54 | ||
|
|
a1461f401a | ||
|
|
f9fd30275c | ||
|
|
eeea14dab4 | ||
|
|
1df807a198 | ||
|
|
0848287069 | ||
|
|
334b37a256 | ||
|
|
8776a3b672 | ||
|
|
657550f767 | ||
|
|
45a0114f75 | ||
|
|
a8ddd3e12a | ||
|
|
b668396151 | ||
|
|
9e7c33369c | ||
|
|
8943ca6d1a | ||
|
|
b3460c1f9d | ||
|
|
5166c9265f | ||
|
|
35c8cfa01d | ||
|
|
8e6dbf3772 | ||
|
|
cb381dfa06 | ||
|
|
80124e3b85 | ||
|
|
7aae27e36f | ||
|
|
b212956fbb | ||
|
|
d033355e84 | ||
|
|
59d7a117d0 | ||
|
|
dfbb38c5f1 | ||
|
|
3f9954144a | ||
|
|
1b84efdbfa | ||
|
|
c33e78a7dc | ||
|
|
747c759bbb | ||
|
|
e0a709676e | ||
|
|
a96275c286 | ||
|
|
ca7c809feb | ||
|
|
a8a0bcb7db | ||
|
|
0e2bf23830 | ||
|
|
44a8b797fb | ||
|
|
cdede298a9 | ||
|
|
f6ac4d3559 | ||
|
|
7e1531dbca | ||
|
|
9aeb422157 | ||
|
|
fba0f8cf8e | ||
|
|
0f4a5227e1 | ||
|
|
d16a752b43 | ||
|
|
4d9aed90d6 | ||
|
|
4bec27fd49 | ||
|
|
f1720b7bbc | ||
|
|
7a79a18d8f | ||
|
|
472034c950 | ||
|
|
b50ab56f9e | ||
|
|
26d30663c4 | ||
|
|
ffecc390eb | ||
|
|
aae5c56a31 | ||
|
|
6670446037 | ||
|
|
bd39b07bb5 | ||
|
|
a9438b6121 | ||
|
|
f546cf4521 | ||
|
|
43efa78750 | ||
|
|
9e3de43802 | ||
|
|
d7931777bc | ||
|
|
bb48d55bd4 | ||
|
|
610b02968e | ||
|
|
a7112c42df | ||
|
|
8a474a3b00 | ||
|
|
59e18d155e | ||
|
|
ff790eeefb | ||
|
|
97e918da03 | ||
|
|
4f30998873 | ||
|
|
a0fc478a28 | ||
|
|
bb03fd1968 | ||
|
|
0c65f74662 | ||
|
|
29ac2bd4f5 | ||
|
|
0d8b4f32e8 | ||
|
|
83db7f24eb | ||
|
|
5f9976a193 | ||
|
|
9480ef485b | ||
|
|
a9b7432584 | ||
|
|
6d7081910f | ||
|
|
aa96089ab4 | ||
|
|
58ae681417 | ||
|
|
c038e0af5e | ||
|
|
4f9976c0cb | ||
|
|
e308e5ed57 | ||
|
|
7b1b1ca994 | ||
|
|
551be49a1a | ||
|
|
a3888e6902 | ||
|
|
43bb6a0dd8 | ||
|
|
05863acbcd | ||
|
|
0e4cd06137 | ||
|
|
85d2d974e7 | ||
|
|
3f021f9552 | ||
|
|
4688870984 |
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.java text eol=lf
|
||||
14
.github/PULL_REQUEST_TEMPLATE.md
vendored
14
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,16 +1,16 @@
|
||||
# Description
|
||||
** Describe your change here**
|
||||
# Description
|
||||
|
||||
<!-- Describe your change here -->
|
||||
|
||||
# Before submitting a PR:
|
||||
We love getting PRs, but we hate asking people for the same basic changes every time.
|
||||
We love getting PRs, but we hate asking people for the same basic changes every time.
|
||||
|
||||
- [ ] Push your changes to a branch other than `master`. Create your PR from that branch.
|
||||
- [ ] Push your changes to a branch other than `main`. Create your PR from that branch.
|
||||
- [ ] Add JavaDocs and other comments
|
||||
- [ ] Write tests that run and pass in CI. See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to capture snapshot data.
|
||||
- [ ] Run `mvn clean compile` locally. This may reformat your code, commit those changes.
|
||||
- [ ] Run `mvn -D enable-ci clean install site` locally. If this command doesn't succeed, your change will not pass CI.
|
||||
|
||||
# When creating a PR:
|
||||
|
||||
- [ ] Fill in the "Description" above.
|
||||
- [ ] Enable "Allow edits from maintainers".
|
||||
- [ ] Fill in the "Description" above.
|
||||
- [ ] Enable "Allow edits from maintainers".
|
||||
|
||||
67
.github/workflows/codeql-analysis.yml
vendored
Normal file
67
.github/workflows/codeql-analysis.yml
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main, gh-pages ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ main ]
|
||||
schedule:
|
||||
- cron: '20 0 * * 6'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'java' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
|
||||
# Learn more:
|
||||
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
48
.github/workflows/maven-build.yml
vendored
48
.github/workflows/maven-build.yml
vendored
@@ -2,42 +2,51 @@ name: CI
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
# this is required by spotless for JDK 16+
|
||||
env:
|
||||
JAVA_11_PLUS_MAVEN_OPTS: "--add-opens jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-opens jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED --add-opens jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED --add-opens jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED --add-opens jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: build-only (Java ${{ matrix.java }})
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
java: [ 13 ]
|
||||
java: [ 16 ]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v1
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: ${{ matrix.java }}
|
||||
distribution: 'adopt'
|
||||
- name: Cached .m2
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v2.1.6
|
||||
with:
|
||||
path: ~/.m2/repository
|
||||
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-maven-
|
||||
- name: Maven Install (skipTests)
|
||||
run: mvn -B install -DskipTests -D enable-ci --file pom.xml
|
||||
env:
|
||||
MAVEN_OPTS: ${{ env.JAVA_11_PLUS_MAVEN_OPTS }}
|
||||
run: mvn -B install -DskipTests --file pom.xml
|
||||
site:
|
||||
name: site (Java ${{ matrix.java }})
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
java: [ 8, 11 ]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v1
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: ${{ matrix.java }}
|
||||
- uses: actions/cache@v2
|
||||
distribution: 'adopt'
|
||||
- uses: actions/cache@v2.1.6
|
||||
with:
|
||||
path: ~/.m2/repository
|
||||
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
|
||||
@@ -49,24 +58,41 @@ jobs:
|
||||
name: test (${{ matrix.os }}, Java ${{ matrix.java }})
|
||||
runs-on: ${{ matrix.os }}-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ ubuntu, windows ]
|
||||
java: [ 8, 11, 13, 15-ea ]
|
||||
java: [ 8, 11, 16 ]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v1
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: ${{ matrix.java }}
|
||||
- uses: actions/cache@v2
|
||||
distribution: 'adopt'
|
||||
- uses: actions/cache@v2.1.6
|
||||
with:
|
||||
path: ~/.m2/repository
|
||||
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-maven-
|
||||
# JDK 8
|
||||
- name: Maven Install without Code Coverage
|
||||
if: matrix.os == 'windows'
|
||||
if: matrix.os == 'windows' && matrix.java == '8'
|
||||
run: mvn -B install --file pom.xml
|
||||
- name: Maven Install with Code Coverage
|
||||
if: matrix.os != 'windows'
|
||||
if: matrix.os != 'windows' && matrix.java == '8'
|
||||
run: mvn -B install -D enable-ci --file pom.xml
|
||||
- name: Codecov Report
|
||||
if: matrix.os != 'windows' && matrix.java == '8'
|
||||
uses: codecov/codecov-action@v1.5.0
|
||||
# JDK 11+
|
||||
- name: Maven Install without Code Coverage
|
||||
if: matrix.os == 'windows' && matrix.java != '8'
|
||||
env:
|
||||
MAVEN_OPTS: ${{ env.JAVA_11_PLUS_MAVEN_OPTS }}
|
||||
run: mvn -B install --file pom.xml "-Dsurefire.argLine=--add-opens java.base/java.net=ALL-UNNAMED"
|
||||
- name: Maven Install with Code Coverage
|
||||
if: matrix.os != 'windows' && matrix.java != '8'
|
||||
env:
|
||||
MAVEN_OPTS: ${{ env.JAVA_11_PLUS_MAVEN_OPTS }}
|
||||
run: mvn -B install -D enable-ci --file pom.xml "-Dsurefire.argLine=--add-opens java.base/java.net=ALL-UNNAMED"
|
||||
|
||||
14
.github/workflows/release-drafter.yml
vendored
Normal file
14
.github/workflows/release-drafter.yml
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
|
||||
name: Release Drafter
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
update_release_draft:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Release Drafter
|
||||
uses: release-drafter/release-drafter@v5.15.0
|
||||
@@ -21,7 +21,7 @@ Example for a single test case:
|
||||
|
||||
### Setting up credential
|
||||
|
||||
1. Create an OAuth token on github.com
|
||||
1. Create a "Personal access token" on https://github.com/ (`Settings` > `Developer settings` > `Personal access tokens`)
|
||||
2. Set the GITHUB_OAUTH environment variable to the value of that token
|
||||
3. Set the system property `test.github.useProxy` (usually like "-Dtest.github.useProxy" as a Java VM option)
|
||||
|
||||
@@ -41,7 +41,7 @@ Once you have credentials setup, you add new test classes and test methods as yo
|
||||
|
||||
Keep `useProxy` enabled and iterate on your tests as needed. With `useProxy` enabled your tests will interact with
|
||||
GitHub - you will need to clean up your server-state between runs. This can be done manually to start with.
|
||||
Once your test code is somewhat stable, use `getGitHubBeforeAfter()` to get a `GitHub` instance for test setup and cleanup.
|
||||
Once your test code is somewhat stable, use `getNonRecordingGitHub()` to get a `GitHub` instance for test setup and cleanup.
|
||||
Interactions with that `GitHub` instance will not be recorded as part of the test, keeping the test data files to a minimum.
|
||||
|
||||
#### Running tests against your personal GitHub user account
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
[](https://mvnrepository.com/artifact/org.kohsuke/github-api)
|
||||
[](https://gitter.im/hub4j/github-api?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||

|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
7
codecov.yml
Normal file
7
codecov.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
ignore:
|
||||
- "**/extras/okhttp3/ObsoleteUrlFactory**"
|
||||
- "**/extras/OkHttpConnector"
|
||||
- "**/extras/OkHttp3Connector"
|
||||
- "**/example/**"
|
||||
- "**/github/EnforcementLevel"
|
||||
|
||||
203
pom.xml
203
pom.xml
@@ -2,7 +2,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.kohsuke</groupId>
|
||||
<artifactId>github-api</artifactId>
|
||||
<version>1.118</version>
|
||||
<version>1.130</version>
|
||||
<name>GitHub API for Java</name>
|
||||
<url>https://github-api.kohsuke.org/</url>
|
||||
<description>GitHub API for Java</description>
|
||||
@@ -11,7 +11,7 @@
|
||||
<connection>scm:git:git@github.com/hub4j/${project.artifactId}.git</connection>
|
||||
<developerConnection>scm:git:ssh://git@github.com/hub4j/${project.artifactId}.git</developerConnection>
|
||||
<url>https://github.com/hub4j/github-api/</url>
|
||||
<tag>github-api-1.118</tag>
|
||||
<tag>github-api-1.130</tag>
|
||||
</scm>
|
||||
|
||||
<distributionManagement>
|
||||
@@ -33,18 +33,21 @@
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<spotbugs-maven-plugin.version>4.1.4</spotbugs-maven-plugin.version>
|
||||
<spotbugs.version>4.1.3</spotbugs.version>
|
||||
<spotbugs-maven-plugin.version>4.2.3</spotbugs-maven-plugin.version>
|
||||
<spotbugs.version>4.2.3</spotbugs.version>
|
||||
<spotbugs-maven-plugin.failOnError>true</spotbugs-maven-plugin.failOnError>
|
||||
<hamcrest.version>2.2</hamcrest.version>
|
||||
<okhttp3.version>4.4.1</okhttp3.version>
|
||||
<okio.version>2.5.0</okio.version>
|
||||
<spotless-maven-plugin.goal>apply</spotless-maven-plugin.goal>
|
||||
<!-- Using this as the minimum bar for code coverage. Adding methods without covering them will fail this. -->
|
||||
<jacoco.coverage.target.bundle.method>0.60</jacoco.coverage.target.bundle.method>
|
||||
<jacoco.coverage.target.class.method>0.25</jacoco.coverage.target.class.method>
|
||||
<jacoco.coverage.target.bundle.method>0.70</jacoco.coverage.target.bundle.method>
|
||||
<jacoco.coverage.target.class.method>0.50</jacoco.coverage.target.class.method>
|
||||
<!-- For non-ci builds we'd like the build to still complete if jacoco metrics aren't met. -->
|
||||
<jacoco.haltOnFailure>false</jacoco.haltOnFailure>
|
||||
<jjwt.suite.version>0.11.2</jjwt.suite.version>
|
||||
|
||||
<jacoco.surefire.argLine />
|
||||
<surefire.argLine />
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
@@ -99,12 +102,17 @@
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
<version>0.8.6</version>
|
||||
<version>0.8.7</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>prepare-agent</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<propertyName>jacoco.surefire.argLine</propertyName>
|
||||
<!-- no need to get data about external code. It dramatically reduces performance of JaCoCo for nothing -->
|
||||
<include>org.kohsuke.*</include>
|
||||
</configuration>
|
||||
</execution>
|
||||
<!-- attached to Maven test phase -->
|
||||
<execution>
|
||||
@@ -152,59 +160,41 @@
|
||||
<!-- Sample only -->
|
||||
<exclude>org.kohsuke.github.example.*</exclude>
|
||||
|
||||
<!-- No methods -->
|
||||
<exclude>org.kohsuke.github.Previews</exclude>
|
||||
|
||||
<!-- Deprecated -->
|
||||
<exclude>org.kohsuke.github.extras.OkHttpConnector</exclude>
|
||||
<exclude>org.kohsuke.github.extras.OkHttp3Connector</exclude>
|
||||
<exclude>org.kohsuke.github.EnforcementLevel</exclude>
|
||||
<exclude>org.kohsuke.github.GHPerson.1</exclude>
|
||||
|
||||
<!-- These fail coverage on windows because tests are disabled -->
|
||||
<exclude>org.kohsuke.github.GHAsset</exclude>
|
||||
<exclude>org.kohsuke.github.GHReleaseBuilder</exclude>
|
||||
<exclude>org.kohsuke.github.GHRelease</exclude>
|
||||
|
||||
<!-- TODO: Some coverage, but more needed -->
|
||||
<exclude>org.kohsuke.github.GHPullRequestReviewBuilder.DraftReviewComment</exclude>
|
||||
<exclude>org.kohsuke.github.GHIssue.PullRequest</exclude>
|
||||
<exclude>org.kohsuke.github.GHCommitSearchBuilder</exclude>
|
||||
<exclude>org.kohsuke.github.GHRepositorySearchBuilder</exclude>
|
||||
<exclude>org.kohsuke.github.GHUserSearchBuilder</exclude>
|
||||
|
||||
<!-- TODO: These still need test coverage -->
|
||||
<exclude>org.kohsuke.github.GHBranchProtection.RequiredSignatures</exclude>
|
||||
<exclude>org.kohsuke.github.GHBranchProtectionBuilder.Restrictions</exclude>
|
||||
<exclude>org.kohsuke.github.GHBranchProtection.Restrictions</exclude>
|
||||
<exclude>org.kohsuke.github.GHCommentAuthorAssociation</exclude>
|
||||
<exclude>org.kohsuke.github.GHCommitBuilder.UserInfo</exclude>
|
||||
<exclude>org.kohsuke.github.GHCommitState</exclude>
|
||||
<exclude>org.kohsuke.github.GHCompare.Commit</exclude>
|
||||
<exclude>org.kohsuke.github.GHCompare.InnerCommit</exclude>
|
||||
<exclude>org.kohsuke.github.GHCompare.Status</exclude>
|
||||
<exclude>org.kohsuke.github.GHCompare.Tree</exclude>
|
||||
<exclude>org.kohsuke.github.GHCompare.User</exclude>
|
||||
<exclude>org.kohsuke.github.GHCompare</exclude>
|
||||
<exclude>org.kohsuke.github.GHDeployKey</exclude>
|
||||
<exclude>org.kohsuke.github.GHDeploymentStatusBuilder</exclude>
|
||||
<exclude>org.kohsuke.github.GHDirection</exclude>
|
||||
<exclude>org.kohsuke.github.GHEmail</exclude>
|
||||
<exclude>org.kohsuke.github.GHEventPayload.Ping</exclude>
|
||||
<exclude>org.kohsuke.github.GHEventPayload.Release</exclude>
|
||||
<exclude>org.kohsuke.github.GHException</exclude>
|
||||
<exclude>org.kohsuke.github.GHHook</exclude>
|
||||
<exclude>org.kohsuke.github.GHHooks.OrgContext</exclude>
|
||||
<exclude>org.kohsuke.github.GHInvitation</exclude>
|
||||
<exclude>org.kohsuke.github.GHMilestoneState</exclude>
|
||||
<exclude>org.kohsuke.github.GHOrgHook</exclude>
|
||||
<exclude>org.kohsuke.github.GHProject.ProjectStateFilter</exclude>
|
||||
<exclude>org.kohsuke.github.GHPullRequestCommitDetail.Authorship</exclude>
|
||||
<exclude>org.kohsuke.github.GHPullRequestCommitDetail.Commit</exclude>
|
||||
<exclude>org.kohsuke.github.GHPullRequestCommitDetail.CommitPointer</exclude>
|
||||
<exclude>org.kohsuke.github.GHPullRequestCommitDetail.Tree</exclude>
|
||||
<exclude>org.kohsuke.github.GHPullRequestCommitDetail</exclude>
|
||||
<exclude>org.kohsuke.github.GHPullRequestFileDetail</exclude>
|
||||
<exclude>org.kohsuke.github.GHPullRequestQueryBuilder.Sort</exclude>
|
||||
<exclude>org.kohsuke.github.GHReleaseUpdater</exclude>
|
||||
<exclude>org.kohsuke.github.GHRepository.ForkSort</exclude>
|
||||
<exclude>org.kohsuke.github.GHRequestedAction</exclude>
|
||||
<exclude>org.kohsuke.github.GHStargazer</exclude>
|
||||
<exclude>org.kohsuke.github.GHTagObject</exclude>
|
||||
<exclude>org.kohsuke.github.GHTeam.Role</exclude>
|
||||
<exclude>org.kohsuke.github.GHUserSearchBuilder.Sort</exclude>
|
||||
<exclude>org.kohsuke.github.GHVerifiedKey</exclude>
|
||||
</excludes>
|
||||
</rule>
|
||||
@@ -216,7 +206,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>3.2.0</version>
|
||||
<version>3.3.0</version>
|
||||
<configuration>
|
||||
<source>8</source>
|
||||
<failOnWarnings>true</failOnWarnings>
|
||||
@@ -260,7 +250,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-project-info-reports-plugin</artifactId>
|
||||
<version>3.1.1</version>
|
||||
<version>3.1.2</version>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.bcel</groupId>
|
||||
@@ -292,18 +282,7 @@
|
||||
<id>default-test</id>
|
||||
<configuration>
|
||||
<excludesFile>src/test/resources/slow-or-flaky-tests.txt</excludesFile>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>slow-or-flaky-test</id>
|
||||
<phase>test</phase>
|
||||
<goals>
|
||||
<goal>test</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<rerunFailingTestsCount>2</rerunFailingTestsCount>
|
||||
<!-- There are some tests that take longer or are a little flaky. Run them here. -->
|
||||
<includesFile>src/test/resources/slow-or-flaky-tests.txt</includesFile>
|
||||
<argLine>@{jacoco.surefire.argLine} ${surefire.argLine}</argLine>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
@@ -311,7 +290,7 @@
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>animal-sniffer-maven-plugin</artifactId>
|
||||
<version>1.19</version>
|
||||
<version>1.20</version>
|
||||
<configuration>
|
||||
<signature>
|
||||
<groupId>org.codehaus.mojo.signature</groupId>
|
||||
@@ -344,13 +323,14 @@
|
||||
<plugin>
|
||||
<groupId>com.diffplug.spotless</groupId>
|
||||
<artifactId>spotless-maven-plugin</artifactId>
|
||||
<version>2.6.1</version>
|
||||
<version>2.11.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>spotless-check</id>
|
||||
<phase>process-sources</phase>
|
||||
<!-- runs in verify phase by default -->
|
||||
<goals>
|
||||
<goal>${spotless-maven-plugin.goal}</goal>
|
||||
<!-- can be disabled using -Dspotless.check.skip=true -->
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
@@ -409,7 +389,7 @@
|
||||
<dependency>
|
||||
<groupId>com.tngtech.archunit</groupId>
|
||||
<artifactId>archunit</artifactId>
|
||||
<version>0.15.0</version>
|
||||
<version>0.19.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -434,23 +414,29 @@
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.13.1</version>
|
||||
<version>4.13.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.awaitility</groupId>
|
||||
<artifactId>awaitility</artifactId>
|
||||
<version>4.1.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>2.10.2</version>
|
||||
<version>2.12.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>2.4</version>
|
||||
<version>2.8.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.infradna.tool</groupId>
|
||||
<artifactId>bridge-method-annotation</artifactId>
|
||||
<version>1.18</version>
|
||||
<version>1.21</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<!-- for stapler-jetty -->
|
||||
@@ -471,7 +457,7 @@
|
||||
<dependency>
|
||||
<groupId>org.kohsuke.stapler</groupId>
|
||||
<artifactId>stapler</artifactId>
|
||||
<version>1.262</version>
|
||||
<version>1.263</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -483,26 +469,26 @@
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jgit</groupId>
|
||||
<artifactId>org.eclipse.jgit</artifactId>
|
||||
<version>5.10.0.202012080955-r</version>
|
||||
<version>5.11.1.202105131744-r</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-api</artifactId>
|
||||
<version>0.11.2</version>
|
||||
<scope>test</scope>
|
||||
<version>${jjwt.suite.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-impl</artifactId>
|
||||
<version>0.11.2</version>
|
||||
<scope>test</scope>
|
||||
<version>${jjwt.suite.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-jackson</artifactId>
|
||||
<version>0.11.2</version>
|
||||
<scope>test</scope>
|
||||
<version>${jjwt.suite.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okio</groupId>
|
||||
@@ -533,13 +519,13 @@
|
||||
<dependency>
|
||||
<groupId>org.kohsuke</groupId>
|
||||
<artifactId>wordnet-random-name</artifactId>
|
||||
<version>1.3</version>
|
||||
<version>1.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>3.6.28</version>
|
||||
<version>3.10.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -551,13 +537,13 @@
|
||||
<dependency>
|
||||
<groupId>com.github.tomakehurst</groupId>
|
||||
<artifactId>wiremock-jre8-standalone</artifactId>
|
||||
<version>2.27.2</version>
|
||||
<version>2.28.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.8.6</version>
|
||||
<version>2.8.7</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -580,6 +566,38 @@
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
<profiles>
|
||||
<!-- only enable slow-or-flaky-test if -Dtest= is not present -->
|
||||
<profile>
|
||||
<id>slow-or-flaky-test</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>!test</name>
|
||||
</property>
|
||||
</activation>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>slow-or-flaky-test</id>
|
||||
<phase>test</phase>
|
||||
<goals>
|
||||
<goal>test</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<rerunFailingTestsCount>2</rerunFailingTestsCount>
|
||||
<!-- There are some tests that take longer or are a little
|
||||
flaky. Run them here. -->
|
||||
<includesFile>src/test/resources/slow-or-flaky-tests.txt</includesFile>
|
||||
<argLine>@{jacoco.surefire.argLine} ${surefire.argLine}</argLine>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>ci-non-windows</id>
|
||||
<activation>
|
||||
@@ -591,7 +609,8 @@
|
||||
</os>
|
||||
</activation>
|
||||
<properties>
|
||||
<spotless-maven-plugin.goal>check</spotless-maven-plugin.goal>
|
||||
<!-- Only fail code coverage on non-windows machines -->
|
||||
<jacoco.haltOnFailure>true</jacoco.haltOnFailure>
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
@@ -601,23 +620,55 @@
|
||||
<name>enable-ci</name>
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<jacoco.haltOnFailure>true</jacoco.haltOnFailure>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.diffplug.spotless</groupId>
|
||||
<artifactId>spotless-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>spotless-check</id>
|
||||
<!-- In CI, run check early in the build -->
|
||||
<phase>process-sources</phase>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-enforcer-plugin</artifactId>
|
||||
<version>3.0.0-M3</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>enforce-jacoco-exist</id>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>enforce</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<rules>
|
||||
<requireFilesExist>
|
||||
<files>
|
||||
<file>${project.build.directory}/jacoco.exec</file>
|
||||
</files>
|
||||
</requireFilesExist>
|
||||
</rules>
|
||||
<fail>true</fail>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>release</id>
|
||||
<properties>
|
||||
<spotless-maven-plugin.goal>check</spotless-maven-plugin.goal>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import org.kohsuke.github.internal.EnumUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.kohsuke.github.Previews.MACHINE_MAN;
|
||||
import static org.kohsuke.github.internal.Previews.MACHINE_MAN;
|
||||
|
||||
/**
|
||||
* A Github App.
|
||||
@@ -20,7 +23,7 @@ public class GHApp extends GHObject {
|
||||
private String description;
|
||||
private String externalUrl;
|
||||
private Map<String, String> permissions;
|
||||
private List<GHEvent> events;
|
||||
private List<String> events;
|
||||
private long installationsCount;
|
||||
private String htmlUrl;
|
||||
|
||||
@@ -114,7 +117,9 @@ public class GHApp extends GHObject {
|
||||
* @return the events
|
||||
*/
|
||||
public List<GHEvent> getEvents() {
|
||||
return events;
|
||||
return events.stream()
|
||||
.map(e -> EnumUtils.getEnumOrDefault(GHEvent.class, e, GHEvent.UNKNOWN))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,7 +131,7 @@ public class GHApp extends GHObject {
|
||||
*/
|
||||
@Deprecated
|
||||
public void setEvents(List<GHEvent> events) {
|
||||
this.events = events;
|
||||
this.events = events.stream().map(GHEvent::symbol).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,7 +5,7 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.kohsuke.github.Previews.MACHINE_MAN;
|
||||
import static org.kohsuke.github.internal.Previews.MACHINE_MAN;
|
||||
|
||||
/**
|
||||
* Creates a access token for a GitHub App Installation
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.kohsuke.github.internal.EnumUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.kohsuke.github.Previews.GAMBIT;
|
||||
import static org.kohsuke.github.Previews.MACHINE_MAN;
|
||||
import static org.kohsuke.github.internal.Previews.GAMBIT;
|
||||
import static org.kohsuke.github.internal.Previews.MACHINE_MAN;
|
||||
|
||||
/**
|
||||
* A Github App Installation.
|
||||
@@ -35,7 +37,7 @@ public class GHAppInstallation extends GHObject {
|
||||
@JsonProperty("target_type")
|
||||
private GHTargetType targetType;
|
||||
private Map<String, GHPermissionType> permissions;
|
||||
private List<GHEvent> events;
|
||||
private List<String> events;
|
||||
@JsonProperty("single_file_name")
|
||||
private String singleFileName;
|
||||
@JsonProperty("repository_selection")
|
||||
@@ -250,7 +252,9 @@ public class GHAppInstallation extends GHObject {
|
||||
* @return the events
|
||||
*/
|
||||
public List<GHEvent> getEvents() {
|
||||
return events;
|
||||
return events.stream()
|
||||
.map(e -> EnumUtils.getEnumOrDefault(GHEvent.class, e, GHEvent.UNKNOWN))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -262,7 +266,7 @@ public class GHAppInstallation extends GHObject {
|
||||
*/
|
||||
@Deprecated
|
||||
public void setEvents(List<GHEvent> events) {
|
||||
this.events = events;
|
||||
this.events = events.stream().map(GHEvent::symbol).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
140
src/main/java/org/kohsuke/github/GHArtifact.java
Normal file
140
src/main/java/org/kohsuke/github/GHArtifact.java
Normal file
@@ -0,0 +1,140 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.kohsuke.github.function.InputStreamFunction;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Date;
|
||||
import java.util.Objects;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* An artifact from a workflow run.
|
||||
*
|
||||
* @author Guillaume Smet
|
||||
*/
|
||||
public class GHArtifact extends GHObject {
|
||||
|
||||
// Not provided by the API.
|
||||
@JsonIgnore
|
||||
private GHRepository owner;
|
||||
|
||||
private String name;
|
||||
private long sizeInBytes;
|
||||
private String archiveDownloadUrl;
|
||||
private boolean expired;
|
||||
private String expiresAt;
|
||||
|
||||
/**
|
||||
* Gets the name.
|
||||
*
|
||||
* @return the name
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the size of the artifact in bytes.
|
||||
*
|
||||
* @return the size
|
||||
*/
|
||||
public long getSizeInBytes() {
|
||||
return sizeInBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the archive download URL.
|
||||
*
|
||||
* @return the archive download URL
|
||||
*/
|
||||
public URL getArchiveDownloadUrl() {
|
||||
return GitHubClient.parseURL(archiveDownloadUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* If this artifact has expired.
|
||||
*
|
||||
* @return if the artifact has expired
|
||||
*/
|
||||
public boolean isExpired() {
|
||||
return expired;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the date at which this artifact will expire.
|
||||
*
|
||||
* @return the date of expiration
|
||||
*/
|
||||
public Date getExpiresAt() {
|
||||
return GitHubClient.parseDate(expiresAt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Repository to which the artifact belongs.
|
||||
*
|
||||
* @return the repository
|
||||
*/
|
||||
public GHRepository getRepository() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated This object has no HTML URL.
|
||||
*/
|
||||
@Override
|
||||
public URL getHtmlUrl() throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the artifact.
|
||||
*
|
||||
* @throws IOException
|
||||
* the io exception
|
||||
*/
|
||||
public void delete() throws IOException {
|
||||
root.createRequest().method("DELETE").withUrlPath(getApiRoute()).fetchHttpStatusCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads the artifact.
|
||||
*
|
||||
* @param <T>
|
||||
* the type of result
|
||||
* @param streamFunction
|
||||
* The {@link InputStreamFunction} that will process the stream
|
||||
* @throws IOException
|
||||
* The IO exception.
|
||||
* @return the result of reading the stream.
|
||||
*/
|
||||
public <T> T download(InputStreamFunction<T> streamFunction) throws IOException {
|
||||
requireNonNull(streamFunction, "Stream function must not be null");
|
||||
|
||||
return root.createRequest().method("GET").withUrlPath(getApiRoute(), "zip").fetchStream(streamFunction);
|
||||
}
|
||||
|
||||
private String getApiRoute() {
|
||||
if (owner == null) {
|
||||
// Workflow runs returned from search to do not have an owner. Attempt to use url.
|
||||
final URL url = Objects.requireNonNull(getUrl(), "Missing instance URL!");
|
||||
return StringUtils.prependIfMissing(url.toString().replace(root.getApiUrl(), ""), "/");
|
||||
}
|
||||
return "/repos/" + owner.getOwnerName() + "/" + owner.getName() + "/actions/artifacts/" + getId();
|
||||
}
|
||||
|
||||
GHArtifact wrapUp(GHRepository owner) {
|
||||
this.owner = owner;
|
||||
return wrapUp(owner.root);
|
||||
}
|
||||
|
||||
GHArtifact wrapUp(GitHub root) {
|
||||
this.root = root;
|
||||
if (owner != null)
|
||||
owner.wrap(root);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
49
src/main/java/org/kohsuke/github/GHArtifactsIterable.java
Normal file
49
src/main/java/org/kohsuke/github/GHArtifactsIterable.java
Normal file
@@ -0,0 +1,49 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.Iterator;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Iterable for artifacts listing.
|
||||
*/
|
||||
class GHArtifactsIterable extends PagedIterable<GHArtifact> {
|
||||
private final transient GHRepository owner;
|
||||
private final GitHubRequest request;
|
||||
|
||||
private GHArtifactsPage result;
|
||||
|
||||
public GHArtifactsIterable(GHRepository owner, GitHubRequest.Builder<?> requestBuilder) {
|
||||
this.owner = owner;
|
||||
try {
|
||||
this.request = requestBuilder.build();
|
||||
} catch (MalformedURLException e) {
|
||||
throw new GHException("Malformed URL", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public PagedIterator<GHArtifact> _iterator(int pageSize) {
|
||||
return new PagedIterator<>(
|
||||
adapt(GitHubPageIterator.create(owner.getRoot().getClient(), GHArtifactsPage.class, request, pageSize)),
|
||||
null);
|
||||
}
|
||||
|
||||
protected Iterator<GHArtifact[]> adapt(final Iterator<GHArtifactsPage> base) {
|
||||
return new Iterator<GHArtifact[]>() {
|
||||
public boolean hasNext() {
|
||||
return base.hasNext();
|
||||
}
|
||||
|
||||
public GHArtifact[] next() {
|
||||
GHArtifactsPage v = base.next();
|
||||
if (result == null) {
|
||||
result = v;
|
||||
}
|
||||
return v.getArtifacts(owner);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
20
src/main/java/org/kohsuke/github/GHArtifactsPage.java
Normal file
20
src/main/java/org/kohsuke/github/GHArtifactsPage.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
/**
|
||||
* Represents the one page of artifacts result when listing artifacts.
|
||||
*/
|
||||
class GHArtifactsPage {
|
||||
private int total_count;
|
||||
private GHArtifact[] artifacts;
|
||||
|
||||
public int getTotalCount() {
|
||||
return total_count;
|
||||
}
|
||||
|
||||
GHArtifact[] getArtifacts(GHRepository owner) {
|
||||
for (GHArtifact artifact : artifacts) {
|
||||
artifact.wrapUp(owner);
|
||||
}
|
||||
return artifacts;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package org.kohsuke.github;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import org.kohsuke.github.internal.Previews;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
@@ -6,7 +6,7 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
|
||||
import static org.kohsuke.github.Previews.ZZZAX;
|
||||
import static org.kohsuke.github.internal.Previews.ZZZAX;
|
||||
|
||||
/**
|
||||
* The type GHBranchProtection.
|
||||
|
||||
@@ -12,7 +12,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.kohsuke.github.Previews.*;
|
||||
import static org.kohsuke.github.internal.Previews.LUKE_CAGE;
|
||||
|
||||
/**
|
||||
* Builder to configure the branch protection settings.
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
|
||||
import edu.umd.cs.findbugs.annotations.NonNull;
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import org.kohsuke.github.GHWorkflowRun.Conclusion;
|
||||
import org.kohsuke.github.GHWorkflowRun.Status;
|
||||
import org.kohsuke.github.internal.EnumUtils;
|
||||
import org.kohsuke.github.internal.Previews;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
@@ -10,6 +15,7 @@ import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Represents a check run.
|
||||
@@ -79,12 +85,27 @@ public class GHCheckRun extends GHObject {
|
||||
* @return Status of the check run
|
||||
* @see Status
|
||||
*/
|
||||
public String getStatus() {
|
||||
@WithBridgeMethods(value = String.class, adapterMethod = "statusAsStr")
|
||||
public Status getStatus() {
|
||||
return Status.from(status);
|
||||
}
|
||||
|
||||
@SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD", justification = "Bridge method of getStatus")
|
||||
private Object statusAsStr(Status status, Class type) {
|
||||
return status;
|
||||
}
|
||||
|
||||
public static enum Status {
|
||||
QUEUED, IN_PROGRESS, COMPLETED
|
||||
QUEUED, IN_PROGRESS, COMPLETED, UNKNOWN;
|
||||
|
||||
public static Status from(String value) {
|
||||
return EnumUtils.getNullableEnumOrDefault(Status.class, value, Status.UNKNOWN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,7 +114,13 @@ public class GHCheckRun extends GHObject {
|
||||
* @return Status of the check run
|
||||
* @see Conclusion
|
||||
*/
|
||||
public String getConclusion() {
|
||||
@WithBridgeMethods(value = String.class, adapterMethod = "conclusionAsStr")
|
||||
public Conclusion getConclusion() {
|
||||
return Conclusion.from(conclusion);
|
||||
}
|
||||
|
||||
@SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD", justification = "Bridge method of getConclusion")
|
||||
private Object conclusionAsStr(Conclusion conclusion, Class type) {
|
||||
return conclusion;
|
||||
}
|
||||
|
||||
@@ -104,7 +131,16 @@ public class GHCheckRun extends GHObject {
|
||||
* Parameters - <code>conclusion</code></a>.
|
||||
*/
|
||||
public static enum Conclusion {
|
||||
SUCCESS, FAILURE, NEUTRAL, CANCELLED, TIMED_OUT, ACTION_REQUIRED, SKIPPED
|
||||
ACTION_REQUIRED, CANCELLED, FAILURE, NEUTRAL, SUCCESS, SKIPPED, STALE, TIMED_OUT, UNKNOWN;
|
||||
|
||||
public static Conclusion from(String value) {
|
||||
return EnumUtils.getNullableEnumOrDefault(Conclusion.class, value, Conclusion.UNKNOWN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,6 +28,7 @@ import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import edu.umd.cs.findbugs.annotations.CheckForNull;
|
||||
import edu.umd.cs.findbugs.annotations.NonNull;
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import org.kohsuke.github.internal.Previews;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
@@ -151,7 +152,7 @@ public final class GHCheckRunBuilder {
|
||||
}
|
||||
GHCheckRun run = requester.with("output", output).with("actions", actions).fetch(GHCheckRun.class).wrap(repo);
|
||||
while (!extraAnnotations.isEmpty()) {
|
||||
Output output2 = new Output(output.title, output.summary);
|
||||
Output output2 = new Output(output.title, output.summary).withText(output.text);
|
||||
int i = Math.min(extraAnnotations.size(), MAX_ANNOTATIONS);
|
||||
output2.annotations = extraAnnotations.subList(0, i);
|
||||
extraAnnotations = extraAnnotations.subList(i, extraAnnotations.size());
|
||||
|
||||
@@ -8,13 +8,13 @@ import javax.annotation.Nonnull;
|
||||
* Iterable for check-runs listing.
|
||||
*/
|
||||
class GHCheckRunsIterable extends PagedIterable<GHCheckRun> {
|
||||
private final transient GitHub root;
|
||||
private final GHRepository owner;
|
||||
private final GitHubRequest request;
|
||||
|
||||
private GHCheckRunsPage result;
|
||||
|
||||
public GHCheckRunsIterable(GitHub root, GitHubRequest request) {
|
||||
this.root = root;
|
||||
public GHCheckRunsIterable(GHRepository owner, GitHubRequest request) {
|
||||
this.owner = owner;
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ class GHCheckRunsIterable extends PagedIterable<GHCheckRun> {
|
||||
@Override
|
||||
public PagedIterator<GHCheckRun> _iterator(int pageSize) {
|
||||
return new PagedIterator<>(
|
||||
adapt(GitHubPageIterator.create(root.getClient(), GHCheckRunsPage.class, request, pageSize)),
|
||||
adapt(GitHubPageIterator.create(owner.getRoot().getClient(), GHCheckRunsPage.class, request, pageSize)),
|
||||
null);
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ class GHCheckRunsIterable extends PagedIterable<GHCheckRun> {
|
||||
if (result == null) {
|
||||
result = v;
|
||||
}
|
||||
return v.getCheckRuns(root);
|
||||
return v.getCheckRuns(owner);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -11,9 +11,9 @@ class GHCheckRunsPage {
|
||||
return total_count;
|
||||
}
|
||||
|
||||
GHCheckRun[] getCheckRuns(GitHub root) {
|
||||
GHCheckRun[] getCheckRuns(GHRepository owner) {
|
||||
for (GHCheckRun check_run : check_runs) {
|
||||
check_run.wrap(root);
|
||||
check_run.wrap(owner);
|
||||
}
|
||||
return check_runs;
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@ import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import static org.kohsuke.github.Previews.ANTIOPE;
|
||||
import static org.kohsuke.github.Previews.GROOT;
|
||||
import static org.kohsuke.github.internal.Previews.ANTIOPE;
|
||||
import static org.kohsuke.github.internal.Previews.GROOT;
|
||||
|
||||
/**
|
||||
* A commit in a repository.
|
||||
|
||||
@@ -5,7 +5,7 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
import static org.kohsuke.github.Previews.*;
|
||||
import static org.kohsuke.github.internal.Previews.SQUIRREL_GIRL;
|
||||
|
||||
/**
|
||||
* A comment attached to a commit (or a specific line in a specific file of a commit.)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.kohsuke.github.internal.Previews;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
@@ -118,6 +118,41 @@ public class GHContentSearchBuilder extends GHSearchBuilder<GHContent> {
|
||||
return q("repo:" + v);
|
||||
}
|
||||
|
||||
/**
|
||||
* Order gh content search builder.
|
||||
*
|
||||
* @param v
|
||||
* the v
|
||||
* @return the gh content search builder
|
||||
*/
|
||||
public GHContentSearchBuilder order(GHDirection v) {
|
||||
req.with("order", v);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort gh content search builder.
|
||||
*
|
||||
* @param sort
|
||||
* the sort
|
||||
* @return the gh content search builder
|
||||
*/
|
||||
public GHContentSearchBuilder sort(GHContentSearchBuilder.Sort sort) {
|
||||
if (Sort.BEST_MATCH.equals(sort)) {
|
||||
req.remove("sort");
|
||||
} else {
|
||||
req.with("sort", sort);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The enum Sort.
|
||||
*/
|
||||
public enum Sort {
|
||||
BEST_MATCH, INDEXED
|
||||
}
|
||||
|
||||
private static class ContentSearchResult extends SearchResult<GHContent> {
|
||||
private GHContent[] items;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ package org.kohsuke.github;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.kohsuke.github.Previews.BAPTISTE;
|
||||
import static org.kohsuke.github.internal.Previews.BAPTISTE;
|
||||
|
||||
/**
|
||||
* Creates a repository
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import org.kohsuke.github.internal.Previews;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import org.kohsuke.github.internal.Previews;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import org.kohsuke.github.internal.Previews;
|
||||
|
||||
/**
|
||||
* Represents the state of deployment
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import org.kohsuke.github.internal.Previews;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.Locale;
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import org.kohsuke.github.internal.Previews;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.kohsuke.github.internal.Previews;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
@@ -12,6 +12,7 @@ import java.util.Locale;
|
||||
public enum GHEvent {
|
||||
CHECK_RUN,
|
||||
CHECK_SUITE,
|
||||
CODE_SCANNING_ALERT,
|
||||
COMMIT_COMMENT,
|
||||
CONTENT_REFERENCE,
|
||||
CREATE,
|
||||
@@ -19,6 +20,8 @@ public enum GHEvent {
|
||||
DEPLOY_KEY,
|
||||
DEPLOYMENT,
|
||||
DEPLOYMENT_STATUS,
|
||||
DISCUSSION,
|
||||
DISCUSSION_COMMENT,
|
||||
DOWNLOAD,
|
||||
FOLLOW,
|
||||
FORK,
|
||||
@@ -56,6 +59,7 @@ public enum GHEvent {
|
||||
REPOSITORY,
|
||||
REPOSITORY_IMPORT,
|
||||
REPOSITORY_VULNERABILITY_ALERT,
|
||||
SCHEDULE,
|
||||
SECURITY_ADVISORY,
|
||||
STAR,
|
||||
STATUS,
|
||||
@@ -65,6 +69,11 @@ public enum GHEvent {
|
||||
WORKFLOW_DISPATCH,
|
||||
WORKFLOW_RUN,
|
||||
|
||||
/**
|
||||
* Special event type that means we haven't found an enum value corresponding to the event.
|
||||
*/
|
||||
UNKNOWN,
|
||||
|
||||
/**
|
||||
* Special event type that means "every possible event"
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Represents an event.
|
||||
@@ -18,6 +18,16 @@ public class GHEventInfo extends GitHubInteractiveObject {
|
||||
|
||||
private long id;
|
||||
private String created_at;
|
||||
|
||||
/**
|
||||
* Representation of GitHub Event API Event Type.
|
||||
*
|
||||
* This is not the same as the values used for hook methods such as
|
||||
* {@link GHRepository#createHook(String, Map, Collection, boolean)}.
|
||||
*
|
||||
* @see <a href="https://docs.github.com/en/developers/webhooks-and-events/github-event-types">GitHub event
|
||||
* types</a>
|
||||
*/
|
||||
private String type;
|
||||
|
||||
// these are all shallow objects
|
||||
@@ -40,20 +50,45 @@ public class GHEventInfo extends GitHubInteractiveObject {
|
||||
private String name; // owner/repo
|
||||
}
|
||||
|
||||
static final Map<String, GHEvent> mapTypeStringToEvent = createEventMap();
|
||||
|
||||
/**
|
||||
* Map for GitHub Event API Event Type to GHEvent.
|
||||
*
|
||||
* @see <a href="https://docs.github.com/en/developers/webhooks-and-events/github-event-types">GitHub event
|
||||
* types</a>
|
||||
*/
|
||||
private static Map<String, GHEvent> createEventMap() {
|
||||
HashMap<String, GHEvent> map = new HashMap<>();
|
||||
map.put("CommitCommentEvent", GHEvent.COMMIT_COMMENT);
|
||||
map.put("CreateEvent", GHEvent.CREATE);
|
||||
map.put("DeleteEvent", GHEvent.DELETE);
|
||||
map.put("ForkEvent", GHEvent.FORK);
|
||||
map.put("GollumEvent", GHEvent.GOLLUM);
|
||||
map.put("IssueCommentEvent", GHEvent.ISSUE_COMMENT);
|
||||
map.put("IssuesEvent", GHEvent.ISSUES);
|
||||
map.put("MemberEvent", GHEvent.MEMBER);
|
||||
map.put("PublicEvent", GHEvent.PUBLIC);
|
||||
map.put("PullRequestEvent", GHEvent.PULL_REQUEST);
|
||||
map.put("PullRequestReviewEvent", GHEvent.PULL_REQUEST_REVIEW);
|
||||
map.put("PullRequestReviewCommentEvent", GHEvent.PULL_REQUEST_REVIEW_COMMENT);
|
||||
map.put("PushEvent", GHEvent.PUSH);
|
||||
map.put("ReleaseEvent", GHEvent.RELEASE);
|
||||
map.put("WatchEvent", GHEvent.WATCH);
|
||||
return Collections.unmodifiableMap(map);
|
||||
}
|
||||
|
||||
static GHEvent transformTypeToGHEvent(String type) {
|
||||
return mapTypeStringToEvent.getOrDefault(type, GHEvent.UNKNOWN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets type.
|
||||
*
|
||||
* @return the type
|
||||
*/
|
||||
public GHEvent getType() {
|
||||
String t = type;
|
||||
if (t.endsWith("Event"))
|
||||
t = t.substring(0, t.length() - 5);
|
||||
for (GHEvent e : GHEvent.values()) {
|
||||
if (e.name().replace("_", "").equalsIgnoreCase(t))
|
||||
return e;
|
||||
}
|
||||
return null; // unknown event type
|
||||
return transformTypeToGHEvent(type);
|
||||
}
|
||||
|
||||
GHEventInfo wrapUp(GitHub root) {
|
||||
|
||||
@@ -5,7 +5,9 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Base type for types used in databinding of the event payload.
|
||||
@@ -380,9 +382,9 @@ public class GHEventPayload extends GitHubInteractiveObject {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets label.
|
||||
* Gets the added or removed label for labeled/unlabeled events.
|
||||
*
|
||||
* @return the label
|
||||
* @return label the added or removed label
|
||||
*/
|
||||
public GHLabel getLabel() {
|
||||
return label;
|
||||
@@ -519,6 +521,10 @@ public class GHEventPayload extends GitHubInteractiveObject {
|
||||
public static class Issue extends GHEventPayload {
|
||||
private GHIssue issue;
|
||||
|
||||
private GHLabel label;
|
||||
|
||||
private GHIssueChanges changes;
|
||||
|
||||
/**
|
||||
* Gets issue.
|
||||
*
|
||||
@@ -538,6 +544,24 @@ public class GHEventPayload extends GitHubInteractiveObject {
|
||||
this.issue = issue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the added or removed label for labeled/unlabeled events.
|
||||
*
|
||||
* @return label the added or removed label
|
||||
*/
|
||||
public GHLabel getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get changes (for action="edited")
|
||||
*
|
||||
* @return changes
|
||||
*/
|
||||
public GHIssueChanges getChanges() {
|
||||
return changes;
|
||||
}
|
||||
|
||||
@Override
|
||||
void wrapUp(GitHub root) {
|
||||
super.wrapUp(root);
|
||||
@@ -687,9 +711,11 @@ public class GHEventPayload extends GitHubInteractiveObject {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets master branch.
|
||||
* Gets default branch.
|
||||
*
|
||||
* @return the master branch
|
||||
* Name is an artifact of when "master" was the most common default.
|
||||
*
|
||||
* @return the default branch
|
||||
*/
|
||||
@SuppressFBWarnings(value = "UWF_UNWRITTEN_FIELD", justification = "Comes from JSON deserialization")
|
||||
public String getMasterBranch() {
|
||||
@@ -935,7 +961,7 @@ public class GHEventPayload extends GitHubInteractiveObject {
|
||||
}
|
||||
|
||||
/**
|
||||
* The full Git ref that was pushed. Example: “refs/heads/master”
|
||||
* The full Git ref that was pushed. Example: “refs/heads/main”
|
||||
*
|
||||
* @return the ref
|
||||
*/
|
||||
@@ -1067,7 +1093,7 @@ public class GHEventPayload extends GitHubInteractiveObject {
|
||||
public static class PushCommit {
|
||||
private GitUser author;
|
||||
private GitUser committer;
|
||||
private String url, sha, message;
|
||||
private String url, sha, message, timestamp;
|
||||
private boolean distinct;
|
||||
private List<String> added, removed, modified;
|
||||
|
||||
@@ -1156,6 +1182,15 @@ public class GHEventPayload extends GitHubInteractiveObject {
|
||||
public List<String> getModified() {
|
||||
return modified;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the timestamp of the commit
|
||||
*
|
||||
* @return the timestamp
|
||||
*/
|
||||
public Date getTimestamp() {
|
||||
return GitHubClient.parseDate(timestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1294,4 +1329,125 @@ public class GHEventPayload extends GitHubInteractiveObject {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Occurs when someone triggered a workflow run or sends a POST request to the "Create a workflow dispatch event"
|
||||
* endpoint.
|
||||
*
|
||||
* @see <a href=
|
||||
* "https://docs.github.com/en/developers/webhooks-and-events/webhook-events-and-payloads#workflow_dispatch">
|
||||
* workflow dispatch event</a>
|
||||
* @see <a href=
|
||||
* "https://docs.github.com/en/actions/reference/events-that-trigger-workflows#workflow_dispatch">Events that
|
||||
* trigger workflows</a>
|
||||
*/
|
||||
public static class WorkflowDispatch extends GHEventPayload {
|
||||
private Map<String, Object> inputs;
|
||||
private String ref;
|
||||
private String workflow;
|
||||
|
||||
/**
|
||||
* Gets the map of input parameters passed to the workflow.
|
||||
*
|
||||
* @return the map of input parameters
|
||||
*/
|
||||
public Map<String, Object> getInputs() {
|
||||
return inputs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ref of the branch (e.g. refs/heads/main)
|
||||
*
|
||||
* @return the ref of the branch
|
||||
*/
|
||||
public String getRef() {
|
||||
return ref;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the path of the workflow file (e.g. .github/workflows/hello-world-workflow.yml).
|
||||
*
|
||||
* @return the path of the workflow file
|
||||
*/
|
||||
public String getWorkflow() {
|
||||
return workflow;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A workflow run was requested or completed.
|
||||
*
|
||||
* @see <a href=
|
||||
* "https://docs.github.com/en/developers/webhooks-and-events/webhook-events-and-payloads#workflow_run">
|
||||
* workflow run event</a>
|
||||
* @see <a href="https://docs.github.com/en/rest/reference/actions#workflow-runs">Actions Workflow Runs</a>
|
||||
*/
|
||||
public static class WorkflowRun extends GHEventPayload {
|
||||
private GHWorkflowRun workflowRun;
|
||||
private GHWorkflow workflow;
|
||||
|
||||
/**
|
||||
* Gets the workflow run.
|
||||
*
|
||||
* @return the workflow run
|
||||
*/
|
||||
public GHWorkflowRun getWorkflowRun() {
|
||||
return workflowRun;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the associated workflow.
|
||||
*
|
||||
* @return the associated workflow
|
||||
*/
|
||||
public GHWorkflow getWorkflow() {
|
||||
return workflow;
|
||||
}
|
||||
|
||||
@Override
|
||||
void wrapUp(GitHub root) {
|
||||
super.wrapUp(root);
|
||||
if (workflowRun == null || workflow == null) {
|
||||
throw new IllegalStateException(
|
||||
"Expected workflow and workflow_run payload, but got something else. Maybe we've got another type of event?");
|
||||
}
|
||||
GHRepository repository = getRepository();
|
||||
if (repository == null) {
|
||||
throw new IllegalStateException("Repository must not be null");
|
||||
}
|
||||
workflowRun.wrapUp(repository);
|
||||
workflow.wrapUp(repository);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A label was created, edited or deleted.
|
||||
*
|
||||
* @see <a href= "https://docs.github.com/en/developers/webhooks-and-events/webhook-events-and-payloads#label">
|
||||
* label event</a>
|
||||
*/
|
||||
public static class Label extends GHEventPayload {
|
||||
|
||||
private GHLabel label;
|
||||
|
||||
private GHLabelChanges changes;
|
||||
|
||||
/**
|
||||
* Gets the label.
|
||||
*
|
||||
* @return the label
|
||||
*/
|
||||
public GHLabel getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets changes (for action="edited")
|
||||
*
|
||||
* @return changes
|
||||
*/
|
||||
public GHLabelChanges getChanges() {
|
||||
return changes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import org.kohsuke.github.internal.EnumUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -40,10 +40,7 @@ public abstract class GHHook extends GHObject {
|
||||
public EnumSet<GHEvent> getEvents() {
|
||||
EnumSet<GHEvent> s = EnumSet.noneOf(GHEvent.class);
|
||||
for (String e : events) {
|
||||
if (e.equals("*"))
|
||||
s.add(GHEvent.ALL);
|
||||
else
|
||||
s.add(Enum.valueOf(GHEvent.class, e.toUpperCase(Locale.ENGLISH)));
|
||||
s.add(e.equals("*") ? GHEvent.ALL : EnumUtils.getEnumOrDefault(GHEvent.class, e, GHEvent.UNKNOWN));
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.kohsuke.github.Previews.SQUIRREL_GIRL;
|
||||
import static org.kohsuke.github.internal.Previews.SQUIRREL_GIRL;
|
||||
|
||||
/**
|
||||
* Represents an issue on GitHub.
|
||||
@@ -312,7 +312,7 @@ public class GHIssue extends GHObject implements Reactable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets labels.
|
||||
* Sets labels on the target to a specific list.
|
||||
*
|
||||
* @param labels
|
||||
* the labels
|
||||
@@ -326,100 +326,137 @@ public class GHIssue extends GHObject implements Reactable {
|
||||
/**
|
||||
* Adds labels to the issue.
|
||||
*
|
||||
* Labels that are already present on the target are ignored.
|
||||
*
|
||||
* @return the complete list of labels including the new additions
|
||||
* @param names
|
||||
* Names of the label
|
||||
* @throws IOException
|
||||
* the io exception
|
||||
*/
|
||||
public void addLabels(String... names) throws IOException {
|
||||
_addLabels(Arrays.asList(names));
|
||||
@WithBridgeMethods(void.class)
|
||||
public List<GHLabel> addLabels(String... names) throws IOException {
|
||||
return _addLabels(Arrays.asList(names));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add labels.
|
||||
*
|
||||
* Labels that are already present on the target are ignored.
|
||||
*
|
||||
* @return the complete list of labels including the new additions
|
||||
* @param labels
|
||||
* the labels
|
||||
* @throws IOException
|
||||
* the io exception
|
||||
*/
|
||||
public void addLabels(GHLabel... labels) throws IOException {
|
||||
addLabels(Arrays.asList(labels));
|
||||
@WithBridgeMethods(void.class)
|
||||
public List<GHLabel> addLabels(GHLabel... labels) throws IOException {
|
||||
return addLabels(Arrays.asList(labels));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add labels.
|
||||
*
|
||||
* Labels that are already present on the target are ignored.
|
||||
*
|
||||
* @return the complete list of labels including the new additions
|
||||
* @param labels
|
||||
* the labels
|
||||
* @throws IOException
|
||||
* the io exception
|
||||
*/
|
||||
public void addLabels(Collection<GHLabel> labels) throws IOException {
|
||||
_addLabels(GHLabel.toNames(labels));
|
||||
@WithBridgeMethods(void.class)
|
||||
public List<GHLabel> addLabels(Collection<GHLabel> labels) throws IOException {
|
||||
return _addLabels(GHLabel.toNames(labels));
|
||||
}
|
||||
|
||||
private void _addLabels(Collection<String> names) throws IOException {
|
||||
List<String> newLabels = new ArrayList<String>();
|
||||
|
||||
for (GHLabel label : getLabels()) {
|
||||
newLabels.add(label.getName());
|
||||
}
|
||||
for (String name : names) {
|
||||
if (!newLabels.contains(name)) {
|
||||
newLabels.add(name);
|
||||
}
|
||||
}
|
||||
setLabels(newLabels.toArray(new String[0]));
|
||||
private List<GHLabel> _addLabels(Collection<String> names) throws IOException {
|
||||
return Arrays.asList(root.createRequest()
|
||||
.with("labels", names)
|
||||
.method("POST")
|
||||
.withUrlPath(getIssuesApiRoute() + "/labels")
|
||||
.fetch(GHLabel[].class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a given label by name from this issue.
|
||||
* Remove a single label.
|
||||
*
|
||||
* Attempting to remove a label that is not present throws {@link GHFileNotFoundException}.
|
||||
*
|
||||
* @return the remaining list of labels
|
||||
* @param name
|
||||
* the name
|
||||
* @throws IOException
|
||||
* the io exception, throws {@link GHFileNotFoundException} if label was not present.
|
||||
*/
|
||||
@WithBridgeMethods(void.class)
|
||||
public List<GHLabel> removeLabel(String name) throws IOException {
|
||||
return Arrays.asList(root.createRequest()
|
||||
.method("DELETE")
|
||||
.withUrlPath(getIssuesApiRoute() + "/labels", name)
|
||||
.fetch(GHLabel[].class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a collection of labels.
|
||||
*
|
||||
* Attempting to remove labels that are not present on the target are ignored.
|
||||
*
|
||||
* @return the remaining list of labels
|
||||
* @param names
|
||||
* the names
|
||||
* @throws IOException
|
||||
* the io exception
|
||||
*/
|
||||
public void removeLabels(String... names) throws IOException {
|
||||
_removeLabels(Arrays.asList(names));
|
||||
@WithBridgeMethods(void.class)
|
||||
public List<GHLabel> removeLabels(String... names) throws IOException {
|
||||
return _removeLabels(Arrays.asList(names));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove labels.
|
||||
* Remove a collection of labels.
|
||||
*
|
||||
* Attempting to remove labels that are not present on the target are ignored.
|
||||
*
|
||||
* @return the remaining list of labels
|
||||
* @param labels
|
||||
* the labels
|
||||
* @throws IOException
|
||||
* the io exception
|
||||
* @see #removeLabels(String...) #removeLabels(String...)
|
||||
*/
|
||||
public void removeLabels(GHLabel... labels) throws IOException {
|
||||
removeLabels(Arrays.asList(labels));
|
||||
@WithBridgeMethods(void.class)
|
||||
public List<GHLabel> removeLabels(GHLabel... labels) throws IOException {
|
||||
return removeLabels(Arrays.asList(labels));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove labels.
|
||||
* Remove a collection of labels.
|
||||
*
|
||||
* Attempting to remove labels that are not present on the target are ignored.
|
||||
*
|
||||
* @return the remaining list of labels
|
||||
* @param labels
|
||||
* the labels
|
||||
* @throws IOException
|
||||
* the io exception
|
||||
*/
|
||||
public void removeLabels(Collection<GHLabel> labels) throws IOException {
|
||||
_removeLabels(GHLabel.toNames(labels));
|
||||
@WithBridgeMethods(void.class)
|
||||
public List<GHLabel> removeLabels(Collection<GHLabel> labels) throws IOException {
|
||||
return _removeLabels(GHLabel.toNames(labels));
|
||||
}
|
||||
|
||||
private void _removeLabels(Collection<String> names) throws IOException {
|
||||
List<String> newLabels = new ArrayList<String>();
|
||||
|
||||
for (GHLabel l : getLabels()) {
|
||||
if (!names.contains(l.getName())) {
|
||||
newLabels.add(l.getName());
|
||||
private List<GHLabel> _removeLabels(Collection<String> names) throws IOException {
|
||||
List<GHLabel> remainingLabels = Collections.emptyList();
|
||||
for (String name : names) {
|
||||
try {
|
||||
remainingLabels = removeLabel(name);
|
||||
} catch (GHFileNotFoundException e) {
|
||||
// when trying to remove multiple labels, we ignore already removed
|
||||
}
|
||||
}
|
||||
|
||||
setLabels(newLabels.toArray(new String[0]));
|
||||
return remainingLabels;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
49
src/main/java/org/kohsuke/github/GHIssueChanges.java
Normal file
49
src/main/java/org/kohsuke/github/GHIssueChanges.java
Normal file
@@ -0,0 +1,49 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
|
||||
/**
|
||||
* Wrapper to define changed fields on issues action="edited"
|
||||
*
|
||||
* @see GHEventPayload.Issue
|
||||
*/
|
||||
@SuppressFBWarnings("UWF_UNWRITTEN_FIELD")
|
||||
public class GHIssueChanges {
|
||||
|
||||
private GHFrom title;
|
||||
private GHFrom body;
|
||||
|
||||
/**
|
||||
* Old issue title.
|
||||
*
|
||||
* @return old issue title (or null if not changed)
|
||||
*/
|
||||
public GHFrom getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Old issue body.
|
||||
*
|
||||
* @return old issue body (or null if not changed)
|
||||
*/
|
||||
public GHFrom getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for changed values.
|
||||
*/
|
||||
public static class GHFrom {
|
||||
private String from;
|
||||
|
||||
/**
|
||||
* Previous value that was changed.
|
||||
*
|
||||
* @return previous value
|
||||
*/
|
||||
public String getFrom() {
|
||||
return from;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ package org.kohsuke.github;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
import static org.kohsuke.github.Previews.*;
|
||||
import static org.kohsuke.github.internal.Previews.SQUIRREL_GIRL;
|
||||
|
||||
/**
|
||||
* Comment to the issue
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.kohsuke.github;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JacksonInject;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
@@ -22,6 +23,11 @@ import javax.annotation.Nonnull;
|
||||
*/
|
||||
public class GHLabel extends GitHubInteractiveObject {
|
||||
|
||||
private long id;
|
||||
private String nodeId;
|
||||
@JsonProperty("default")
|
||||
private boolean default_;
|
||||
|
||||
@Nonnull
|
||||
private String url, name, color;
|
||||
|
||||
@@ -42,6 +48,24 @@ public class GHLabel extends GitHubInteractiveObject {
|
||||
return Objects.requireNonNull(root);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets id.
|
||||
*
|
||||
* @return the id
|
||||
*/
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets node id.
|
||||
*
|
||||
* @return the node id.
|
||||
*/
|
||||
public String getNodeId() {
|
||||
return nodeId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets url.
|
||||
*
|
||||
@@ -82,6 +106,15 @@ public class GHLabel extends GitHubInteractiveObject {
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the label is one of the default labels created by GitHub automatically.
|
||||
*
|
||||
* @return true if the label is a default one
|
||||
*/
|
||||
public boolean isDefault() {
|
||||
return default_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets color.
|
||||
*
|
||||
|
||||
49
src/main/java/org/kohsuke/github/GHLabelChanges.java
Normal file
49
src/main/java/org/kohsuke/github/GHLabelChanges.java
Normal file
@@ -0,0 +1,49 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
|
||||
/**
|
||||
* Wrapper to define changed fields on label action="edited"
|
||||
*
|
||||
* @see GHEventPayload.Label
|
||||
*/
|
||||
@SuppressFBWarnings("UWF_UNWRITTEN_FIELD")
|
||||
public class GHLabelChanges {
|
||||
|
||||
private GHFrom name;
|
||||
private GHFrom color;
|
||||
|
||||
/**
|
||||
* Old label name.
|
||||
*
|
||||
* @return old label name (or null if not changed)
|
||||
*/
|
||||
public GHFrom getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Old label color.
|
||||
*
|
||||
* @return old label color (or null if not changed)
|
||||
*/
|
||||
public GHFrom getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for changed values.
|
||||
*/
|
||||
public static class GHFrom {
|
||||
private String from;
|
||||
|
||||
/**
|
||||
* Previous value that was changed.
|
||||
*
|
||||
* @return previous value
|
||||
*/
|
||||
public String getFrom() {
|
||||
return from;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,9 @@ public class GHMeta {
|
||||
private List<String> api;
|
||||
private List<String> pages;
|
||||
private List<String> importer = new ArrayList<>();
|
||||
private List<String> packages;
|
||||
private List<String> actions;
|
||||
private List<String> dependabot;
|
||||
|
||||
/**
|
||||
* Is verifiable password authentication boolean.
|
||||
@@ -86,4 +89,31 @@ public class GHMeta {
|
||||
public List<String> getImporter() {
|
||||
return Collections.unmodifiableList(importer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets package.
|
||||
*
|
||||
* @return the package
|
||||
*/
|
||||
public List<String> getPackages() {
|
||||
return Collections.unmodifiableList(packages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets actions.
|
||||
*
|
||||
* @return the actions
|
||||
*/
|
||||
public List<String> getActions() {
|
||||
return Collections.unmodifiableList(actions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets dependabot.
|
||||
*
|
||||
* @return the dependabot
|
||||
*/
|
||||
public List<String> getDependabot() {
|
||||
return Collections.unmodifiableList(dependabot);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import static org.kohsuke.github.Previews.INERTIA;
|
||||
import static org.kohsuke.github.internal.Previews.INERTIA;
|
||||
|
||||
/**
|
||||
* The type GHOrganization.
|
||||
@@ -18,6 +18,9 @@ import static org.kohsuke.github.Previews.INERTIA;
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
public class GHOrganization extends GHPerson {
|
||||
|
||||
private boolean has_organization_projects;
|
||||
|
||||
GHOrganization wrapUp(GitHub root) {
|
||||
return (GHOrganization) super.wrapUp(root);
|
||||
}
|
||||
@@ -367,6 +370,35 @@ public class GHOrganization extends GHPerson {
|
||||
root.createRequest().method("DELETE").withUrlPath("/orgs/" + login + "/public_members/" + u.getLogin()).send();
|
||||
}
|
||||
|
||||
/**
|
||||
* Are projects enabled for organization boolean.
|
||||
*
|
||||
* @return the boolean
|
||||
*/
|
||||
public boolean areOrganizationProjectsEnabled() {
|
||||
return has_organization_projects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets organization projects enabled status boolean
|
||||
*
|
||||
* @param newStatus
|
||||
* enable status
|
||||
* @throws IOException
|
||||
* the io exception
|
||||
*/
|
||||
public void enableOrganizationProjects(boolean newStatus) throws IOException {
|
||||
edit("has_organization_projects", newStatus);
|
||||
}
|
||||
|
||||
private void edit(String key, Object value) throws IOException {
|
||||
root.createRequest()
|
||||
.withUrlPath(String.format("/orgs/%s", login))
|
||||
.method("PATCH")
|
||||
.with(key, value)
|
||||
.fetchInto(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the projects for this organization.
|
||||
*
|
||||
|
||||
@@ -28,7 +28,7 @@ import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Locale;
|
||||
|
||||
import static org.kohsuke.github.Previews.INERTIA;
|
||||
import static org.kohsuke.github.internal.Previews.INERTIA;
|
||||
|
||||
/**
|
||||
* A GitHub project.
|
||||
|
||||
@@ -6,7 +6,7 @@ import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
import static org.kohsuke.github.Previews.INERTIA;
|
||||
import static org.kohsuke.github.internal.Previews.INERTIA;
|
||||
|
||||
/**
|
||||
* The type GHProjectCard.
|
||||
|
||||
@@ -4,7 +4,7 @@ import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
import static org.kohsuke.github.Previews.INERTIA;
|
||||
import static org.kohsuke.github.internal.Previews.INERTIA;
|
||||
|
||||
/**
|
||||
* The type GHProjectColumn.
|
||||
|
||||
@@ -36,8 +36,8 @@ import java.util.Objects;
|
||||
|
||||
import javax.annotation.CheckForNull;
|
||||
|
||||
import static org.kohsuke.github.Previews.LYDIAN;
|
||||
import static org.kohsuke.github.Previews.SHADOW_CAT;
|
||||
import static org.kohsuke.github.internal.Previews.LYDIAN;
|
||||
import static org.kohsuke.github.internal.Previews.SHADOW_CAT;
|
||||
|
||||
/**
|
||||
* A pull request.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import static org.kohsuke.github.Previews.SHADOW_CAT;
|
||||
import static org.kohsuke.github.internal.Previews.SHADOW_CAT;
|
||||
|
||||
/**
|
||||
* Lists up pull requests with some filtering and sorting.
|
||||
|
||||
@@ -28,7 +28,7 @@ import java.net.URL;
|
||||
|
||||
import javax.annotation.CheckForNull;
|
||||
|
||||
import static org.kohsuke.github.Previews.*;
|
||||
import static org.kohsuke.github.internal.Previews.SQUIRREL_GIRL;
|
||||
|
||||
/**
|
||||
* Review comment to the pull request
|
||||
@@ -153,7 +153,20 @@ public class GHPullRequestReviewComment extends GHObject implements Reactable {
|
||||
* @return the api route
|
||||
*/
|
||||
protected String getApiRoute() {
|
||||
return "/repos/" + owner.getRepository().getFullName() + "/pulls/comments/" + getId();
|
||||
return getApiRoute(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets api route.
|
||||
*
|
||||
* @param includePullNumber
|
||||
* if true, includes the owning pull request's number in the route.
|
||||
*
|
||||
* @return the api route
|
||||
*/
|
||||
protected String getApiRoute(boolean includePullNumber) {
|
||||
return "/repos/" + owner.getRepository().getFullName() + "/pulls"
|
||||
+ (includePullNumber ? "/" + owner.getNumber() : "") + "/comments/" + getId();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -192,8 +205,7 @@ public class GHPullRequestReviewComment extends GHObject implements Reactable {
|
||||
return owner.root.createRequest()
|
||||
.method("POST")
|
||||
.with("body", body)
|
||||
.with("in_reply_to", getId())
|
||||
.withUrlPath(getApiRoute() + "/comments")
|
||||
.withUrlPath(getApiRoute(true) + "/replies")
|
||||
.fetch(GHPullRequestReviewComment.class)
|
||||
.wrapUp(owner);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.Date;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.CheckForNull;
|
||||
@@ -344,7 +345,7 @@ public class GHRateLimit {
|
||||
private static final UnknownLimitRecord DEFAULT = new UnknownLimitRecord(Long.MIN_VALUE);
|
||||
|
||||
// The starting current UnknownLimitRecord is an expired record.
|
||||
private static UnknownLimitRecord current = DEFAULT;
|
||||
private static final AtomicReference<UnknownLimitRecord> current = new AtomicReference<>(DEFAULT);
|
||||
|
||||
/**
|
||||
* Create a new unknown record that resets at the specified time.
|
||||
@@ -356,18 +357,20 @@ public class GHRateLimit {
|
||||
super(unknownLimit, unknownRemaining, resetEpochSeconds);
|
||||
}
|
||||
|
||||
static synchronized Record current() {
|
||||
if (current.isExpired()) {
|
||||
current = new UnknownLimitRecord(System.currentTimeMillis() / 1000L + unknownLimitResetSeconds);
|
||||
static Record current() {
|
||||
Record result = current.get();
|
||||
if (result.isExpired()) {
|
||||
current.set(new UnknownLimitRecord(System.currentTimeMillis() / 1000L + unknownLimitResetSeconds));
|
||||
result = current.get();
|
||||
}
|
||||
return current;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the current UnknownLimitRecord. For use during testing only.
|
||||
*/
|
||||
static synchronized void reset() {
|
||||
current = DEFAULT;
|
||||
static void reset() {
|
||||
current.set(DEFAULT);
|
||||
unknownLimitResetSeconds = defaultUnknownLimitResetSeconds;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package org.kohsuke.github;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
import static org.kohsuke.github.Previews.*;
|
||||
import static org.kohsuke.github.internal.Previews.SQUIRREL_GIRL;
|
||||
|
||||
/**
|
||||
* Reaction to issue, comment, PR, and so on.
|
||||
|
||||
@@ -41,7 +41,7 @@ public class GHReleaseBuilder {
|
||||
* Specifies the commitish value that determines where the Git tag is created from. Can be any branch or commit SHA.
|
||||
*
|
||||
* @param commitish
|
||||
* Defaults to the repository’s default branch (usually "master"). Unused if the Git tag already exists.
|
||||
* Defaults to the repository’s default branch (usually "main"). Unused if the Git tag already exists.
|
||||
* @return the gh release builder
|
||||
*/
|
||||
public GHReleaseBuilder commitish(String commitish) {
|
||||
|
||||
@@ -45,7 +45,7 @@ public class GHReleaseUpdater {
|
||||
* Specifies the commitish value that determines where the Git tag is created from. Can be any branch or commit SHA.
|
||||
*
|
||||
* @param commitish
|
||||
* Defaults to the repository’s default branch (usually "master"). Unused if the Git tag already exists.
|
||||
* Defaults to the repository’s default branch (usually "main"). Unused if the Git tag already exists.
|
||||
* @return the gh release updater
|
||||
*/
|
||||
public GHReleaseUpdater commitish(String commitish) {
|
||||
|
||||
@@ -30,6 +30,8 @@ import edu.umd.cs.findbugs.annotations.CheckForNull;
|
||||
import edu.umd.cs.findbugs.annotations.NonNull;
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.kohsuke.github.function.InputStreamFunction;
|
||||
import org.kohsuke.github.internal.EnumUtils;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
@@ -48,16 +50,24 @@ import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import static java.util.Arrays.*;
|
||||
import static org.kohsuke.github.Previews.*;
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.kohsuke.github.internal.Previews.ANTIOPE;
|
||||
import static org.kohsuke.github.internal.Previews.ANT_MAN;
|
||||
import static org.kohsuke.github.internal.Previews.BAPTISTE;
|
||||
import static org.kohsuke.github.internal.Previews.FLASH;
|
||||
import static org.kohsuke.github.internal.Previews.INERTIA;
|
||||
import static org.kohsuke.github.internal.Previews.MERCY;
|
||||
import static org.kohsuke.github.internal.Previews.NEBULA;
|
||||
import static org.kohsuke.github.internal.Previews.SHADOW_CAT;
|
||||
|
||||
/**
|
||||
* A repository on GitHub.
|
||||
@@ -97,6 +107,8 @@ public class GHRepository extends GHObject {
|
||||
@JsonProperty("private")
|
||||
private boolean _private;
|
||||
|
||||
private String visibility;
|
||||
|
||||
private int forks_count, stargazers_count, watchers_count, size, open_issues_count, subscribers_count;
|
||||
|
||||
private String pushed_at;
|
||||
@@ -449,7 +461,7 @@ public class GHRepository extends GHObject {
|
||||
* Creates a named ref, such as tag, branch, etc.
|
||||
*
|
||||
* @param name
|
||||
* The name of the fully qualified reference (ie: refs/heads/master). If it doesn't start with 'refs' and
|
||||
* The name of the fully qualified reference (ie: refs/heads/main). If it doesn't start with 'refs' and
|
||||
* have at least two slashes, it will be rejected.
|
||||
* @param sha
|
||||
* The SHA1 value to set this reference to
|
||||
@@ -566,7 +578,15 @@ public class GHRepository extends GHObject {
|
||||
* the io exception
|
||||
*/
|
||||
public Map<String, Long> listLanguages() throws IOException {
|
||||
return root.createRequest().withUrlPath(getApiTailUrl("languages")).fetch(HashMap.class);
|
||||
HashMap<String, Long> result = new HashMap<>();
|
||||
root.createRequest().withUrlPath(getApiTailUrl("languages")).fetch(HashMap.class).forEach((key, value) -> {
|
||||
Long addValue = -1L;
|
||||
if (value instanceof Integer) {
|
||||
addValue = Long.valueOf((Integer) value);
|
||||
}
|
||||
result.put(key.toString(), addValue);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -703,6 +723,41 @@ public class GHRepository extends GHObject {
|
||||
return _private;
|
||||
}
|
||||
|
||||
/**
|
||||
* Visibility of a repository.
|
||||
*/
|
||||
public enum Visibility {
|
||||
PUBLIC, INTERNAL, PRIVATE, UNKNOWN;
|
||||
|
||||
public static Visibility from(String value) {
|
||||
return EnumUtils.getNullableEnumOrDefault(Visibility.class, value, Visibility.UNKNOWN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the visibility of the repository.
|
||||
*
|
||||
* @return the visibility
|
||||
*/
|
||||
@Deprecated
|
||||
@Preview(NEBULA)
|
||||
public Visibility getVisibility() {
|
||||
if (visibility == null) {
|
||||
try {
|
||||
populate();
|
||||
} catch (final IOException e) {
|
||||
// Convert this to a runtime exception to avoid messy method signature
|
||||
throw new GHException("Could not populate the visibility of the repository", e);
|
||||
}
|
||||
}
|
||||
return Visibility.from(visibility);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is template boolean.
|
||||
*
|
||||
@@ -793,16 +848,18 @@ public class GHRepository extends GHObject {
|
||||
/**
|
||||
* Returns the primary branch you'll configure in the "Admin > Options" config page.
|
||||
*
|
||||
* @return This field is null until the user explicitly configures the master branch.
|
||||
* @return This field is null until the user explicitly configures the default branch.
|
||||
*/
|
||||
public String getDefaultBranch() {
|
||||
return default_branch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets master branch.
|
||||
* Gets default branch.
|
||||
*
|
||||
* @return the master branch
|
||||
* Name is an artifact of when "master" was the most common default.
|
||||
*
|
||||
* @return the default branch
|
||||
* @deprecated Renamed to {@link #getDefaultBranch()}
|
||||
*/
|
||||
@Deprecated
|
||||
@@ -1195,6 +1252,26 @@ public class GHRepository extends GHObject {
|
||||
set().private_(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets visibility.
|
||||
*
|
||||
* @param value
|
||||
* the value
|
||||
* @throws IOException
|
||||
* the io exception
|
||||
*/
|
||||
@Deprecated
|
||||
@Preview(NEBULA)
|
||||
public void setVisibility(final Visibility value) throws IOException {
|
||||
root.createRequest()
|
||||
.method("PATCH")
|
||||
.withPreview(NEBULA)
|
||||
.with("name", name)
|
||||
.with("visibility", value)
|
||||
.withUrlPath(getApiTailUrl(""))
|
||||
.send();
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow squash merge.
|
||||
*
|
||||
@@ -1700,7 +1777,7 @@ public class GHRepository extends GHObject {
|
||||
* Retrive a tree of the given type for the current GitHub repository.
|
||||
*
|
||||
* @param sha
|
||||
* sha number or branch name ex: "master"
|
||||
* sha number or branch name ex: "main"
|
||||
* @return refs matching the request type
|
||||
* @throws IOException
|
||||
* on failure communicating with GitHub, potentially due to an invalid tree type being requested
|
||||
@@ -1724,7 +1801,7 @@ public class GHRepository extends GHObject {
|
||||
* https://developer.github.com/v3/git/trees/#get-a-tree-recursively
|
||||
*
|
||||
* @param sha
|
||||
* sha number or branch name ex: "master"
|
||||
* sha number or branch name ex: "main"
|
||||
* @param recursive
|
||||
* use 1
|
||||
* @return the tree recursive
|
||||
@@ -1782,7 +1859,7 @@ public class GHRepository extends GHObject {
|
||||
return root.createRequest()
|
||||
.withHeader("Accept", "application/vnd.github.v3.raw")
|
||||
.withUrlPath(target)
|
||||
.fetchStream();
|
||||
.fetchStream(Requester::copyInputStream);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1943,7 +2020,7 @@ public class GHRepository extends GHObject {
|
||||
.withUrlPath(String.format("/repos/%s/%s/commits/%s/check-runs", getOwnerName(), name, ref))
|
||||
.withPreview(ANTIOPE)
|
||||
.build();
|
||||
return new GHCheckRunsIterable(root, request);
|
||||
return new GHCheckRunsIterable(this, request);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2809,7 +2886,7 @@ public class GHRepository extends GHObject {
|
||||
.with("mode", mode == null ? null : mode.toString())
|
||||
.with("context", getFullName())
|
||||
.withUrlPath("/markdown")
|
||||
.fetchStream(),
|
||||
.fetchStream(Requester::copyInputStream),
|
||||
"UTF-8");
|
||||
}
|
||||
|
||||
@@ -2897,6 +2974,110 @@ public class GHRepository extends GHObject {
|
||||
.wrapUp(root);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists all the workflows of this repository.
|
||||
*
|
||||
* @return the paged iterable
|
||||
*/
|
||||
public PagedIterable<GHWorkflow> listWorkflows() {
|
||||
return new GHWorkflowsIterable(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a workflow by id.
|
||||
*
|
||||
* @param id
|
||||
* the id of the workflow run
|
||||
* @return the workflow run
|
||||
* @throws IOException
|
||||
* the io exception
|
||||
*/
|
||||
public GHWorkflow getWorkflow(long id) throws IOException {
|
||||
return getWorkflow(String.valueOf(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a workflow by name of the file.
|
||||
*
|
||||
* @param nameOrId
|
||||
* either the name of the file (e.g. my-workflow.yml) or the id as a string
|
||||
* @return the workflow run
|
||||
* @throws IOException
|
||||
* the io exception
|
||||
*/
|
||||
public GHWorkflow getWorkflow(String nameOrId) throws IOException {
|
||||
return root.createRequest()
|
||||
.withUrlPath(getApiTailUrl("actions/workflows"), nameOrId)
|
||||
.fetch(GHWorkflow.class)
|
||||
.wrapUp(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves workflow runs.
|
||||
*
|
||||
* @return the workflow run query builder
|
||||
*/
|
||||
public GHWorkflowRunQueryBuilder queryWorkflowRuns() {
|
||||
return new GHWorkflowRunQueryBuilder(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a workflow run.
|
||||
*
|
||||
* @param id
|
||||
* the id of the workflow run
|
||||
* @return the workflow run
|
||||
* @throws IOException
|
||||
* the io exception
|
||||
*/
|
||||
public GHWorkflowRun getWorkflowRun(long id) throws IOException {
|
||||
return root.createRequest()
|
||||
.withUrlPath(getApiTailUrl("actions/runs"), String.valueOf(id))
|
||||
.fetch(GHWorkflowRun.class)
|
||||
.wrapUp(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists all the artifacts of this repository.
|
||||
*
|
||||
* @return the paged iterable
|
||||
*/
|
||||
public PagedIterable<GHArtifact> listArtifacts() {
|
||||
return new GHArtifactsIterable(this, root.createRequest().withUrlPath(getApiTailUrl("actions/artifacts")));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an artifact by id.
|
||||
*
|
||||
* @param id
|
||||
* the id of the artifact
|
||||
* @return the artifact
|
||||
* @throws IOException
|
||||
* the io exception
|
||||
*/
|
||||
public GHArtifact getArtifact(long id) throws IOException {
|
||||
return root.createRequest()
|
||||
.withUrlPath(getApiTailUrl("actions/artifacts"), String.valueOf(id))
|
||||
.fetch(GHArtifact.class)
|
||||
.wrapUp(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a job from a workflow run by id.
|
||||
*
|
||||
* @param id
|
||||
* the id of the job
|
||||
* @return the job
|
||||
* @throws IOException
|
||||
* the io exception
|
||||
*/
|
||||
public GHWorkflowJob getWorkflowJob(long id) throws IOException {
|
||||
return root.createRequest()
|
||||
.withUrlPath(getApiTailUrl("/actions/jobs"), String.valueOf(id))
|
||||
.fetch(GHWorkflowJob.class)
|
||||
.wrapUp(this);
|
||||
}
|
||||
|
||||
// Only used within listTopics().
|
||||
private static class Topics {
|
||||
public List<String> names;
|
||||
@@ -2963,6 +3144,52 @@ public class GHRepository extends GHObject {
|
||||
.wrap(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Streams a zip archive of the repository, optionally at a given <code>ref</code>.
|
||||
*
|
||||
* @param <T>
|
||||
* the type of result
|
||||
* @param streamFunction
|
||||
* The {@link InputStreamFunction} that will process the stream
|
||||
* @param ref
|
||||
* if <code>null</code> the repository's default branch, usually <code>main</code>,
|
||||
* @throws IOException
|
||||
* The IO exception.
|
||||
* @return the result of reading the stream.
|
||||
*/
|
||||
public <T> T readZip(InputStreamFunction<T> streamFunction, String ref) throws IOException {
|
||||
return downloadArchive("zip", ref, streamFunction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Streams a tar archive of the repository, optionally at a given <code>ref</code>.
|
||||
*
|
||||
* @param <T>
|
||||
* the type of result
|
||||
* @param streamFunction
|
||||
* The {@link InputStreamFunction} that will process the stream
|
||||
* @param ref
|
||||
* if <code>null</code> the repository's default branch, usually <code>main</code>,
|
||||
* @throws IOException
|
||||
* The IO exception.
|
||||
* @return the result of reading the stream.
|
||||
*/
|
||||
public <T> T readTar(InputStreamFunction<T> streamFunction, String ref) throws IOException {
|
||||
return downloadArchive("tar", ref, streamFunction);
|
||||
}
|
||||
|
||||
private <T> T downloadArchive(@Nonnull String type,
|
||||
@CheckForNull String ref,
|
||||
@Nonnull InputStreamFunction<T> streamFunction) throws IOException {
|
||||
requireNonNull(streamFunction, "Sink must not be null");
|
||||
String tailUrl = getApiTailUrl(type + "ball");
|
||||
if (ref != null) {
|
||||
tailUrl += "/" + ref;
|
||||
}
|
||||
final Requester builder = root.createRequest().method("GET").withUrlPath(tailUrl);
|
||||
return builder.fetchStream(streamFunction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate this object.
|
||||
*
|
||||
@@ -2974,18 +3201,24 @@ public class GHRepository extends GHObject {
|
||||
return; // can't populate if the root is offline
|
||||
}
|
||||
|
||||
final URL url = Objects.requireNonNull(getUrl(), "Missing instance URL!");
|
||||
final URL url = requireNonNull(getUrl(), "Missing instance URL!");
|
||||
|
||||
try {
|
||||
// IMPORTANT: the url for repository records does not reliably point to the API url.
|
||||
// There is bug in Push event payloads that returns the wrong url.
|
||||
// All other occurrences of "url" take the form "https://api.github.com/...".
|
||||
// For Push event repository records, they take the form "https://github.com/{fullName}".
|
||||
root.createRequest().withPreview(BAPTISTE).setRawUrlPath(url.toString()).fetchInto(this).wrap(root);
|
||||
root.createRequest()
|
||||
.withPreview(BAPTISTE)
|
||||
.withPreview(NEBULA)
|
||||
.setRawUrlPath(url.toString())
|
||||
.fetchInto(this)
|
||||
.wrap(root);
|
||||
} catch (HttpException e) {
|
||||
if (e.getCause() instanceof JsonParseException) {
|
||||
root.createRequest()
|
||||
.withPreview(BAPTISTE)
|
||||
.withPreview(NEBULA)
|
||||
.withUrlPath("/repos/" + full_name)
|
||||
.fetchInto(this)
|
||||
.wrap(root);
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import org.kohsuke.github.GHRepository.Visibility;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
import static org.kohsuke.github.Previews.BAPTISTE;
|
||||
import static org.kohsuke.github.internal.Previews.BAPTISTE;
|
||||
import static org.kohsuke.github.internal.Previews.NEBULA;
|
||||
|
||||
abstract class GHRepositoryBuilder<S> extends AbstractBuilder<GHRepository, S> {
|
||||
|
||||
@@ -146,6 +149,20 @@ abstract class GHRepositoryBuilder<S> extends AbstractBuilder<GHRepository, S> {
|
||||
return with("private", enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the repository visibility
|
||||
*
|
||||
* @param visibility
|
||||
* visibility of repository
|
||||
* @return a builder to continue with building
|
||||
* @throws IOException
|
||||
* In case of any networking error or error from the server.
|
||||
*/
|
||||
public S visibility(final Visibility visibility) throws IOException {
|
||||
requester.withPreview(NEBULA);
|
||||
return with("visibility", visibility);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables issue tracker
|
||||
*
|
||||
|
||||
158
src/main/java/org/kohsuke/github/GHWorkflow.java
Normal file
158
src/main/java/org/kohsuke/github/GHWorkflow.java
Normal file
@@ -0,0 +1,158 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A workflow.
|
||||
*
|
||||
* @author Guillaume Smet
|
||||
* @see GHRepository#getWorkflow(long)
|
||||
*/
|
||||
public class GHWorkflow extends GHObject {
|
||||
|
||||
// Not provided by the API.
|
||||
@JsonIgnore
|
||||
private GHRepository owner;
|
||||
|
||||
private String name;
|
||||
private String path;
|
||||
private String state;
|
||||
|
||||
private String htmlUrl;
|
||||
private String badgeUrl;
|
||||
|
||||
/**
|
||||
* The name of the workflow.
|
||||
*
|
||||
* @return the name
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* The path of the workflow e.g. .github/workflows/blank.yaml
|
||||
*
|
||||
* @return the path
|
||||
*/
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* The state of the workflow.
|
||||
*
|
||||
* @return the state
|
||||
*/
|
||||
public String getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getHtmlUrl() throws IOException {
|
||||
return GitHubClient.parseURL(htmlUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Repository to which the workflow belongs.
|
||||
*
|
||||
* @return the repository
|
||||
*/
|
||||
public GHRepository getRepository() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
/**
|
||||
* The badge URL, like https://github.com/octo-org/octo-repo/workflows/CI/badge.svg
|
||||
*
|
||||
* @return the badge url
|
||||
*/
|
||||
public URL getBadgeUrl() {
|
||||
return GitHubClient.parseURL(badgeUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable the workflow.
|
||||
*
|
||||
* @throws IOException
|
||||
* the io exception
|
||||
*/
|
||||
public void disable() throws IOException {
|
||||
root.createRequest().method("PUT").withUrlPath(getApiRoute(), "disable").fetchHttpStatusCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable the workflow.
|
||||
*
|
||||
* @throws IOException
|
||||
* the io exception
|
||||
*/
|
||||
public void enable() throws IOException {
|
||||
root.createRequest().method("PUT").withUrlPath(getApiRoute(), "enable").fetchHttpStatusCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a workflow dispatch event which triggers a manual workflow run.
|
||||
*
|
||||
* @param ref
|
||||
* the git reference for the workflow. The reference can be a branch or tag name.
|
||||
* @throws IOException
|
||||
* the io exception
|
||||
*/
|
||||
public void dispatch(String ref) throws IOException {
|
||||
dispatch(ref, Collections.emptyMap());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a workflow dispatch event which triggers a manual workflow run.
|
||||
*
|
||||
* @param ref
|
||||
* the git reference for the workflow. The reference can be a branch or tag name.
|
||||
* @param inputs
|
||||
* input keys and values configured in the workflow file. The maximum number of properties is 10. Any
|
||||
* default properties configured in the workflow file will be used when inputs are omitted.
|
||||
* @throws IOException
|
||||
* the io exception
|
||||
*/
|
||||
public void dispatch(String ref, Map<String, Object> inputs) throws IOException {
|
||||
Requester requester = root.createRequest()
|
||||
.method("POST")
|
||||
.withUrlPath(getApiRoute(), "dispatches")
|
||||
.with("ref", ref);
|
||||
|
||||
if (!inputs.isEmpty()) {
|
||||
requester.with("inputs", inputs);
|
||||
}
|
||||
|
||||
requester.fetchHttpStatusCode();
|
||||
}
|
||||
|
||||
private String getApiRoute() {
|
||||
if (owner == null) {
|
||||
// Workflow runs returned from search to do not have an owner. Attempt to use url.
|
||||
final URL url = Objects.requireNonNull(getUrl(), "Missing instance URL!");
|
||||
return StringUtils.prependIfMissing(url.toString().replace(root.getApiUrl(), ""), "/");
|
||||
|
||||
}
|
||||
return "/repos/" + owner.getOwnerName() + "/" + owner.getName() + "/actions/workflows/" + getId();
|
||||
}
|
||||
|
||||
GHWorkflow wrapUp(GHRepository owner) {
|
||||
this.owner = owner;
|
||||
return wrapUp(owner.root);
|
||||
}
|
||||
|
||||
GHWorkflow wrapUp(GitHub root) {
|
||||
this.root = root;
|
||||
if (owner != null)
|
||||
owner.wrap(root);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
256
src/main/java/org/kohsuke/github/GHWorkflowJob.java
Normal file
256
src/main/java/org/kohsuke/github/GHWorkflowJob.java
Normal file
@@ -0,0 +1,256 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.kohsuke.github.GHWorkflowRun.Conclusion;
|
||||
import org.kohsuke.github.GHWorkflowRun.Status;
|
||||
import org.kohsuke.github.function.InputStreamFunction;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* A workflow run job.
|
||||
*
|
||||
* @author Guillaume Smet
|
||||
*/
|
||||
public class GHWorkflowJob extends GHObject {
|
||||
|
||||
// Not provided by the API.
|
||||
@JsonIgnore
|
||||
private GHRepository owner;
|
||||
|
||||
private String name;
|
||||
|
||||
private String headSha;
|
||||
|
||||
private String startedAt;
|
||||
private String completedAt;
|
||||
|
||||
private String status;
|
||||
private String conclusion;
|
||||
|
||||
private long runId;
|
||||
|
||||
private String htmlUrl;
|
||||
private String checkRunUrl;
|
||||
|
||||
private List<Step> steps = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* The name of the job.
|
||||
*
|
||||
* @return the name
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the HEAD SHA.
|
||||
*
|
||||
* @return sha for the HEAD commit
|
||||
*/
|
||||
public String getHeadSha() {
|
||||
return headSha;
|
||||
}
|
||||
|
||||
/**
|
||||
* When was this job started?
|
||||
*
|
||||
* @return start date
|
||||
*/
|
||||
public Date getStartedAt() {
|
||||
return GitHubClient.parseDate(startedAt);
|
||||
}
|
||||
|
||||
/**
|
||||
* When was this job completed?
|
||||
*
|
||||
* @return completion date
|
||||
*/
|
||||
public Date getCompletedAt() {
|
||||
return GitHubClient.parseDate(completedAt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets status of the job.
|
||||
* <p>
|
||||
* Can be {@code UNKNOWN} if the value returned by GitHub is unknown from the API.
|
||||
*
|
||||
* @return status of the job
|
||||
*/
|
||||
public Status getStatus() {
|
||||
return Status.from(status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the conclusion of the job.
|
||||
* <p>
|
||||
* Can be {@code UNKNOWN} if the value returned by GitHub is unknown from the API.
|
||||
*
|
||||
* @return conclusion of the job
|
||||
*/
|
||||
public Conclusion getConclusion() {
|
||||
return Conclusion.from(conclusion);
|
||||
}
|
||||
|
||||
/**
|
||||
* The run id.
|
||||
*
|
||||
* @return the run id
|
||||
*/
|
||||
public long getRunId() {
|
||||
return runId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getHtmlUrl() {
|
||||
return GitHubClient.parseURL(htmlUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* The check run URL.
|
||||
*
|
||||
* @return the check run url
|
||||
*/
|
||||
public URL getCheckRunUrl() {
|
||||
return GitHubClient.parseURL(checkRunUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the execution steps of this job.
|
||||
*
|
||||
* @return the execution steps
|
||||
*/
|
||||
public List<Step> getSteps() {
|
||||
return steps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Repository to which the job belongs.
|
||||
*
|
||||
* @return the repository
|
||||
*/
|
||||
public GHRepository getRepository() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads the logs.
|
||||
* <p>
|
||||
* The logs are returned as a text file.
|
||||
*
|
||||
* @param <T>
|
||||
* the type of result
|
||||
* @param streamFunction
|
||||
* The {@link InputStreamFunction} that will process the stream
|
||||
* @throws IOException
|
||||
* The IO exception.
|
||||
* @return the result of reading the stream.
|
||||
*/
|
||||
public <T> T downloadLogs(InputStreamFunction<T> streamFunction) throws IOException {
|
||||
requireNonNull(streamFunction, "Stream function must not be null");
|
||||
|
||||
return root.createRequest().method("GET").withUrlPath(getApiRoute(), "logs").fetchStream(streamFunction);
|
||||
}
|
||||
|
||||
private String getApiRoute() {
|
||||
if (owner == null) {
|
||||
// Workflow runs returned from search to do not have an owner. Attempt to use url.
|
||||
final URL url = Objects.requireNonNull(getUrl(), "Missing instance URL!");
|
||||
return StringUtils.prependIfMissing(url.toString().replace(root.getApiUrl(), ""), "/");
|
||||
|
||||
}
|
||||
return "/repos/" + owner.getOwnerName() + "/" + owner.getName() + "/actions/jobs/" + getId();
|
||||
}
|
||||
|
||||
GHWorkflowJob wrapUp(GHRepository owner) {
|
||||
this.owner = owner;
|
||||
return wrapUp(owner.root);
|
||||
}
|
||||
|
||||
GHWorkflowJob wrapUp(GitHub root) {
|
||||
this.root = root;
|
||||
if (owner != null) {
|
||||
owner.wrap(root);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public static class Step {
|
||||
|
||||
private String name;
|
||||
private int number;
|
||||
|
||||
private String startedAt;
|
||||
private String completedAt;
|
||||
|
||||
private String status;
|
||||
private String conclusion;
|
||||
|
||||
/**
|
||||
* Gets the name of the step.
|
||||
*
|
||||
* @return name
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the sequential number of the step.
|
||||
*
|
||||
* @return number
|
||||
*/
|
||||
public int getNumber() {
|
||||
return number;
|
||||
}
|
||||
|
||||
/**
|
||||
* When was this step started?
|
||||
*
|
||||
* @return start date
|
||||
*/
|
||||
public Date getStartedAt() {
|
||||
return GitHubClient.parseDate(startedAt);
|
||||
}
|
||||
|
||||
/**
|
||||
* When was this step completed?
|
||||
*
|
||||
* @return completion date
|
||||
*/
|
||||
public Date getCompletedAt() {
|
||||
return GitHubClient.parseDate(completedAt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets status of the step.
|
||||
* <p>
|
||||
* Can be {@code UNKNOWN} if the value returned by GitHub is unknown from the API.
|
||||
*
|
||||
* @return status of the step
|
||||
*/
|
||||
public Status getStatus() {
|
||||
return Status.from(status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the conclusion of the step.
|
||||
* <p>
|
||||
* Can be {@code UNKNOWN} if the value returned by GitHub is unknown from the API.
|
||||
*
|
||||
* @return conclusion of the step
|
||||
*/
|
||||
public Conclusion getConclusion() {
|
||||
return Conclusion.from(conclusion);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
|
||||
/**
|
||||
* Lists up jobs of a workflow run with some filtering.
|
||||
*
|
||||
* @author Guillaume Smet
|
||||
*/
|
||||
public class GHWorkflowJobQueryBuilder extends GHQueryBuilder<GHWorkflowJob> {
|
||||
private final GHRepository repo;
|
||||
|
||||
GHWorkflowJobQueryBuilder(GHWorkflowRun workflowRun) {
|
||||
super(workflowRun.getRepository().root);
|
||||
this.repo = workflowRun.getRepository();
|
||||
req.withUrlPath(repo.getApiTailUrl("actions/runs"), String.valueOf(workflowRun.getId()), "jobs");
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a filter to only return the jobs of the most recent execution of the workflow run.
|
||||
*
|
||||
* @return the workflow run job query builder
|
||||
*/
|
||||
public GHWorkflowJobQueryBuilder latest() {
|
||||
req.with("filter", "latest");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a filter to return jobs from all executions of this workflow run.
|
||||
*
|
||||
* @return the workflow run job run query builder
|
||||
*/
|
||||
public GHWorkflowJobQueryBuilder all() {
|
||||
req.with("filter", "all");
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PagedIterable<GHWorkflowJob> list() {
|
||||
try {
|
||||
return new GHWorkflowJobsIterable(repo, req.build());
|
||||
} catch (MalformedURLException e) {
|
||||
throw new GHException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
44
src/main/java/org/kohsuke/github/GHWorkflowJobsIterable.java
Normal file
44
src/main/java/org/kohsuke/github/GHWorkflowJobsIterable.java
Normal file
@@ -0,0 +1,44 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Iterable for workflow run jobs listing.
|
||||
*/
|
||||
class GHWorkflowJobsIterable extends PagedIterable<GHWorkflowJob> {
|
||||
private final GHRepository repo;
|
||||
private final GitHubRequest request;
|
||||
|
||||
private GHWorkflowJobsPage result;
|
||||
|
||||
public GHWorkflowJobsIterable(GHRepository repo, GitHubRequest request) {
|
||||
this.repo = repo;
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public PagedIterator<GHWorkflowJob> _iterator(int pageSize) {
|
||||
return new PagedIterator<>(
|
||||
adapt(GitHubPageIterator.create(repo.root.getClient(), GHWorkflowJobsPage.class, request, pageSize)),
|
||||
null);
|
||||
}
|
||||
|
||||
protected Iterator<GHWorkflowJob[]> adapt(final Iterator<GHWorkflowJobsPage> base) {
|
||||
return new Iterator<GHWorkflowJob[]>() {
|
||||
public boolean hasNext() {
|
||||
return base.hasNext();
|
||||
}
|
||||
|
||||
public GHWorkflowJob[] next() {
|
||||
GHWorkflowJobsPage v = base.next();
|
||||
if (result == null) {
|
||||
result = v;
|
||||
}
|
||||
return v.getWorkflowJobs(repo);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
20
src/main/java/org/kohsuke/github/GHWorkflowJobsPage.java
Normal file
20
src/main/java/org/kohsuke/github/GHWorkflowJobsPage.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
/**
|
||||
* Represents the one page of jobs result when listing jobs from a workflow run.
|
||||
*/
|
||||
class GHWorkflowJobsPage {
|
||||
private int total_count;
|
||||
private GHWorkflowJob[] jobs;
|
||||
|
||||
public int getTotalCount() {
|
||||
return total_count;
|
||||
}
|
||||
|
||||
GHWorkflowJob[] getWorkflowJobs(GHRepository repo) {
|
||||
for (GHWorkflowJob job : jobs) {
|
||||
job.wrapUp(repo);
|
||||
}
|
||||
return jobs;
|
||||
}
|
||||
}
|
||||
457
src/main/java/org/kohsuke/github/GHWorkflowRun.java
Normal file
457
src/main/java/org/kohsuke/github/GHWorkflowRun.java
Normal file
@@ -0,0 +1,457 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.kohsuke.github.function.InputStreamFunction;
|
||||
import org.kohsuke.github.internal.EnumUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* A workflow run.
|
||||
*
|
||||
* @author Guillaume Smet
|
||||
* @see GHRepository#getWorkflowRun(long)
|
||||
*/
|
||||
public class GHWorkflowRun extends GHObject {
|
||||
|
||||
@JsonProperty("repository")
|
||||
private GHRepository owner;
|
||||
|
||||
private String name;
|
||||
private long runNumber;
|
||||
private long workflowId;
|
||||
|
||||
private String htmlUrl;
|
||||
private String jobsUrl;
|
||||
private String logsUrl;
|
||||
private String checkSuiteUrl;
|
||||
private String artifactsUrl;
|
||||
private String cancelUrl;
|
||||
private String rerunUrl;
|
||||
private String workflowUrl;
|
||||
|
||||
private String headBranch;
|
||||
private String headSha;
|
||||
private GHRepository headRepository;
|
||||
private HeadCommit headCommit;
|
||||
|
||||
private String event;
|
||||
private String status;
|
||||
private String conclusion;
|
||||
|
||||
private GHPullRequest[] pullRequests;
|
||||
|
||||
/**
|
||||
* The name of the workflow run.
|
||||
*
|
||||
* @return the name
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* The run number.
|
||||
*
|
||||
* @return the run number
|
||||
*/
|
||||
public long getRunNumber() {
|
||||
return runNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* The workflow id.
|
||||
*
|
||||
* @return the workflow id
|
||||
*/
|
||||
public long getWorkflowId() {
|
||||
return workflowId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getHtmlUrl() throws IOException {
|
||||
return GitHubClient.parseURL(htmlUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* The jobs URL, like https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/jobs
|
||||
*
|
||||
* @return the jobs url
|
||||
*/
|
||||
public URL getJobsUrl() {
|
||||
return GitHubClient.parseURL(jobsUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* The logs URL, like https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/logs
|
||||
*
|
||||
* @return the logs url
|
||||
*/
|
||||
public URL getLogsUrl() {
|
||||
return GitHubClient.parseURL(logsUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* The check suite URL, like https://api.github.com/repos/octo-org/octo-repo/check-suites/414944374
|
||||
*
|
||||
* @return the check suite url
|
||||
*/
|
||||
public URL getCheckSuiteUrl() {
|
||||
return GitHubClient.parseURL(checkSuiteUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* The artifacts URL, like https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/artifacts
|
||||
*
|
||||
* @return the artifacts url
|
||||
*/
|
||||
public URL getArtifactsUrl() {
|
||||
return GitHubClient.parseURL(artifactsUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* The cancel URL, like https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/cancel
|
||||
*
|
||||
* @return the cancel url
|
||||
*/
|
||||
public URL getCancelUrl() {
|
||||
return GitHubClient.parseURL(cancelUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* The rerun URL, like https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/rerun
|
||||
*
|
||||
* @return the rerun url
|
||||
*/
|
||||
public URL getRerunUrl() {
|
||||
return GitHubClient.parseURL(rerunUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* The workflow URL, like https://api.github.com/repos/octo-org/octo-repo/actions/workflows/159038
|
||||
*
|
||||
* @return the workflow url
|
||||
*/
|
||||
public URL getWorkflowUrl() {
|
||||
return GitHubClient.parseURL(workflowUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* The head branch name the changes are on.
|
||||
*
|
||||
* @return head branch name
|
||||
*/
|
||||
public String getHeadBranch() {
|
||||
return headBranch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the HEAD SHA.
|
||||
*
|
||||
* @return sha for the HEAD commit
|
||||
*/
|
||||
public String getHeadSha() {
|
||||
return headSha;
|
||||
}
|
||||
|
||||
/**
|
||||
* The commit of current head.
|
||||
*
|
||||
* @return head commit
|
||||
*/
|
||||
public HeadCommit getHeadCommit() {
|
||||
return headCommit;
|
||||
}
|
||||
|
||||
/**
|
||||
* The repository of current head.
|
||||
*
|
||||
* @return head repository
|
||||
*/
|
||||
public GHRepository getHeadRepository() {
|
||||
return headRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of event that triggered the build.
|
||||
*
|
||||
* @return type of event
|
||||
*/
|
||||
public GHEvent getEvent() {
|
||||
return EnumUtils.getNullableEnumOrDefault(GHEvent.class, event, GHEvent.UNKNOWN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets status of the workflow run.
|
||||
* <p>
|
||||
* Can be {@code UNKNOWN} if the value returned by GitHub is unknown from the API.
|
||||
*
|
||||
* @return status of the workflow run
|
||||
*/
|
||||
public Status getStatus() {
|
||||
return Status.from(status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the conclusion of the workflow run.
|
||||
* <p>
|
||||
* Can be {@code UNKNOWN} if the value returned by GitHub is unknown from the API.
|
||||
*
|
||||
* @return conclusion of the workflow run
|
||||
*/
|
||||
public Conclusion getConclusion() {
|
||||
return Conclusion.from(conclusion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Repository to which the workflow run belongs.
|
||||
*
|
||||
* @return the repository
|
||||
*/
|
||||
public GHRepository getRepository() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the pull requests participated in this workflow run.
|
||||
*
|
||||
* Note this field is only populated for events. When getting a {@link GHWorkflowRun} outside of an event, this is
|
||||
* always empty.
|
||||
*
|
||||
* @return the list of {@link GHPullRequest}s for this workflow run. Only populated for events.
|
||||
* @throws IOException
|
||||
* the io exception
|
||||
*/
|
||||
public List<GHPullRequest> getPullRequests() throws IOException {
|
||||
if (pullRequests != null && pullRequests.length != 0) {
|
||||
for (GHPullRequest pullRequest : pullRequests) {
|
||||
// Only refresh if we haven't do so before
|
||||
pullRequest.refresh(pullRequest.getTitle());
|
||||
}
|
||||
return Collections.unmodifiableList(Arrays.asList(pullRequests));
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel the workflow run.
|
||||
*
|
||||
* @throws IOException
|
||||
* the io exception
|
||||
*/
|
||||
public void cancel() throws IOException {
|
||||
root.createRequest().method("POST").withUrlPath(getApiRoute(), "cancel").fetchHttpStatusCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the workflow run.
|
||||
*
|
||||
* @throws IOException
|
||||
* the io exception
|
||||
*/
|
||||
public void delete() throws IOException {
|
||||
root.createRequest().method("DELETE").withUrlPath(getApiRoute()).fetchHttpStatusCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rerun the workflow run.
|
||||
*
|
||||
* @throws IOException
|
||||
* the io exception
|
||||
*/
|
||||
public void rerun() throws IOException {
|
||||
root.createRequest().method("POST").withUrlPath(getApiRoute(), "rerun").fetchHttpStatusCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists the artifacts attached to this workflow run.
|
||||
*
|
||||
* @return the paged iterable
|
||||
*/
|
||||
public PagedIterable<GHArtifact> listArtifacts() {
|
||||
return new GHArtifactsIterable(owner, root.createRequest().withUrlPath(getApiRoute(), "artifacts"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads the logs.
|
||||
* <p>
|
||||
* The logs are in the form of a zip archive.
|
||||
* <p>
|
||||
* Note that the archive is the same as the one downloaded from a workflow run so it contains the logs for all jobs.
|
||||
*
|
||||
* @param <T>
|
||||
* the type of result
|
||||
* @param streamFunction
|
||||
* The {@link InputStreamFunction} that will process the stream
|
||||
* @throws IOException
|
||||
* The IO exception.
|
||||
* @return the result of reading the stream.
|
||||
*/
|
||||
public <T> T downloadLogs(InputStreamFunction<T> streamFunction) throws IOException {
|
||||
requireNonNull(streamFunction, "Stream function must not be null");
|
||||
|
||||
return root.createRequest().method("GET").withUrlPath(getApiRoute(), "logs").fetchStream(streamFunction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the logs.
|
||||
*
|
||||
* @throws IOException
|
||||
* the io exception
|
||||
*/
|
||||
public void deleteLogs() throws IOException {
|
||||
root.createRequest().method("DELETE").withUrlPath(getApiRoute(), "logs").fetchHttpStatusCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of jobs of this workflow run for the last execution.
|
||||
*
|
||||
* @return list of jobs from the last execution
|
||||
*/
|
||||
public PagedIterable<GHWorkflowJob> listJobs() {
|
||||
return new GHWorkflowJobQueryBuilder(this).latest().list();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of jobs from all the executions of this workflow run.
|
||||
*
|
||||
* @return list of jobs from all the executions
|
||||
*/
|
||||
public PagedIterable<GHWorkflowJob> listAllJobs() {
|
||||
return new GHWorkflowJobQueryBuilder(this).all().list();
|
||||
}
|
||||
|
||||
private String getApiRoute() {
|
||||
if (owner == null) {
|
||||
// Workflow runs returned from search to do not have an owner. Attempt to use url.
|
||||
final URL url = Objects.requireNonNull(getUrl(), "Missing instance URL!");
|
||||
return StringUtils.prependIfMissing(url.toString().replace(root.getApiUrl(), ""), "/");
|
||||
|
||||
}
|
||||
return "/repos/" + owner.getOwnerName() + "/" + owner.getName() + "/actions/runs/" + getId();
|
||||
}
|
||||
|
||||
GHWorkflowRun wrapUp(GHRepository owner) {
|
||||
this.owner = owner;
|
||||
return wrapUp(owner.root);
|
||||
}
|
||||
|
||||
GHWorkflowRun wrapUp(GitHub root) {
|
||||
this.root = root;
|
||||
if (owner != null) {
|
||||
owner.wrap(root);
|
||||
if (pullRequests != null) {
|
||||
for (GHPullRequest singlePull : pullRequests) {
|
||||
singlePull.wrap(owner);
|
||||
}
|
||||
}
|
||||
} else if (pullRequests != null) {
|
||||
for (GHPullRequest singlePull : pullRequests) {
|
||||
singlePull.wrap(root);
|
||||
}
|
||||
}
|
||||
if (headRepository != null) {
|
||||
headRepository.wrap(root);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public static class HeadCommit {
|
||||
private String id;
|
||||
private String treeId;
|
||||
private String message;
|
||||
private String timestamp;
|
||||
private GitUser author;
|
||||
private GitUser committer;
|
||||
|
||||
/**
|
||||
* Gets id of the commit
|
||||
*
|
||||
* @return id of the commit
|
||||
*/
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets id of the tree.
|
||||
*
|
||||
* @return id of the tree
|
||||
*/
|
||||
public String getTreeId() {
|
||||
return treeId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets message.
|
||||
*
|
||||
* @return commit message.
|
||||
*/
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets timestamp of the commit.
|
||||
*
|
||||
* @return timestamp of the commit
|
||||
*/
|
||||
public Date getTimestamp() {
|
||||
return GitHubClient.parseDate(timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets author.
|
||||
*
|
||||
* @return the author
|
||||
*/
|
||||
public GitUser getAuthor() {
|
||||
return author;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets committer.
|
||||
*
|
||||
* @return the committer
|
||||
*/
|
||||
public GitUser getCommitter() {
|
||||
return committer;
|
||||
}
|
||||
}
|
||||
|
||||
public static enum Status {
|
||||
QUEUED, IN_PROGRESS, COMPLETED, UNKNOWN;
|
||||
|
||||
public static Status from(String value) {
|
||||
return EnumUtils.getNullableEnumOrDefault(Status.class, value, Status.UNKNOWN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
}
|
||||
|
||||
public static enum Conclusion {
|
||||
ACTION_REQUIRED, CANCELLED, FAILURE, NEUTRAL, SUCCESS, SKIPPED, STALE, TIMED_OUT, UNKNOWN;
|
||||
|
||||
public static Conclusion from(String value) {
|
||||
return EnumUtils.getNullableEnumOrDefault(Conclusion.class, value, Conclusion.UNKNOWN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
}
|
||||
}
|
||||
101
src/main/java/org/kohsuke/github/GHWorkflowRunQueryBuilder.java
Normal file
101
src/main/java/org/kohsuke/github/GHWorkflowRunQueryBuilder.java
Normal file
@@ -0,0 +1,101 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import org.kohsuke.github.GHWorkflowRun.Status;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
|
||||
/**
|
||||
* Lists up workflow runs with some filtering and sorting.
|
||||
*
|
||||
* @author Guillaume Smet
|
||||
* @see GHRepository#queryWorkflowRuns()
|
||||
*/
|
||||
public class GHWorkflowRunQueryBuilder extends GHQueryBuilder<GHWorkflowRun> {
|
||||
private final GHRepository repo;
|
||||
|
||||
GHWorkflowRunQueryBuilder(GHRepository repo) {
|
||||
super(repo.root);
|
||||
this.repo = repo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Actor workflow run query builder.
|
||||
*
|
||||
* @param actor
|
||||
* the actor
|
||||
* @return the gh workflow run query builder
|
||||
*/
|
||||
public GHWorkflowRunQueryBuilder actor(String actor) {
|
||||
req.with("actor", actor);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Actor workflow run query builder.
|
||||
*
|
||||
* @param actor
|
||||
* the actor
|
||||
* @return the gh workflow run query builder
|
||||
*/
|
||||
public GHWorkflowRunQueryBuilder actor(GHUser actor) {
|
||||
req.with("actor", actor.getLogin());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Branch workflow run query builder.
|
||||
*
|
||||
* @param branch
|
||||
* the branch
|
||||
* @return the gh workflow run query builder
|
||||
*/
|
||||
public GHWorkflowRunQueryBuilder branch(String branch) {
|
||||
req.with("branch", branch);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event workflow run query builder.
|
||||
*
|
||||
* @param event
|
||||
* the event
|
||||
* @return the gh workflow run query builder
|
||||
*/
|
||||
public GHWorkflowRunQueryBuilder event(GHEvent event) {
|
||||
req.with("event", event.symbol());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event workflow run query builder.
|
||||
*
|
||||
* @param event
|
||||
* the event
|
||||
* @return the gh workflow run query builder
|
||||
*/
|
||||
public GHWorkflowRunQueryBuilder event(String event) {
|
||||
req.with("event", event);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Status workflow run query builder.
|
||||
*
|
||||
* @param status
|
||||
* the status
|
||||
* @return the gh workflow run query builder
|
||||
*/
|
||||
public GHWorkflowRunQueryBuilder status(Status status) {
|
||||
req.with("status", status.toString());
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PagedIterable<GHWorkflowRun> list() {
|
||||
try {
|
||||
return new GHWorkflowRunsIterable(repo, req.withUrlPath(repo.getApiTailUrl("actions/runs")).build());
|
||||
} catch (MalformedURLException e) {
|
||||
throw new GHException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
45
src/main/java/org/kohsuke/github/GHWorkflowRunsIterable.java
Normal file
45
src/main/java/org/kohsuke/github/GHWorkflowRunsIterable.java
Normal file
@@ -0,0 +1,45 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Iterable for workflow runs listing.
|
||||
*/
|
||||
class GHWorkflowRunsIterable extends PagedIterable<GHWorkflowRun> {
|
||||
private final GHRepository owner;
|
||||
private final GitHubRequest request;
|
||||
|
||||
private GHWorkflowRunsPage result;
|
||||
|
||||
public GHWorkflowRunsIterable(GHRepository owner, GitHubRequest request) {
|
||||
this.owner = owner;
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public PagedIterator<GHWorkflowRun> _iterator(int pageSize) {
|
||||
return new PagedIterator<>(
|
||||
adapt(GitHubPageIterator
|
||||
.create(owner.getRoot().getClient(), GHWorkflowRunsPage.class, request, pageSize)),
|
||||
null);
|
||||
}
|
||||
|
||||
protected Iterator<GHWorkflowRun[]> adapt(final Iterator<GHWorkflowRunsPage> base) {
|
||||
return new Iterator<GHWorkflowRun[]>() {
|
||||
public boolean hasNext() {
|
||||
return base.hasNext();
|
||||
}
|
||||
|
||||
public GHWorkflowRun[] next() {
|
||||
GHWorkflowRunsPage v = base.next();
|
||||
if (result == null) {
|
||||
result = v;
|
||||
}
|
||||
return v.getWorkflowRuns(owner);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
20
src/main/java/org/kohsuke/github/GHWorkflowRunsPage.java
Normal file
20
src/main/java/org/kohsuke/github/GHWorkflowRunsPage.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
/**
|
||||
* Represents the one page of workflow runs result when listing workflow runs.
|
||||
*/
|
||||
class GHWorkflowRunsPage {
|
||||
private int totalCount;
|
||||
private GHWorkflowRun[] workflowRuns;
|
||||
|
||||
public int getTotalCount() {
|
||||
return totalCount;
|
||||
}
|
||||
|
||||
GHWorkflowRun[] getWorkflowRuns(GHRepository owner) {
|
||||
for (GHWorkflowRun workflowRun : workflowRuns) {
|
||||
workflowRun.wrapUp(owner);
|
||||
}
|
||||
return workflowRuns;
|
||||
}
|
||||
}
|
||||
53
src/main/java/org/kohsuke/github/GHWorkflowsIterable.java
Normal file
53
src/main/java/org/kohsuke/github/GHWorkflowsIterable.java
Normal file
@@ -0,0 +1,53 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.Iterator;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Iterable for workflows listing.
|
||||
*/
|
||||
class GHWorkflowsIterable extends PagedIterable<GHWorkflow> {
|
||||
private final transient GHRepository owner;
|
||||
|
||||
private GHWorkflowsPage result;
|
||||
|
||||
public GHWorkflowsIterable(GHRepository owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public PagedIterator<GHWorkflow> _iterator(int pageSize) {
|
||||
try {
|
||||
GitHubRequest request = owner.getRoot()
|
||||
.createRequest()
|
||||
.withUrlPath(owner.getApiTailUrl("actions/workflows"))
|
||||
.build();
|
||||
|
||||
return new PagedIterator<>(
|
||||
adapt(GitHubPageIterator
|
||||
.create(owner.getRoot().getClient(), GHWorkflowsPage.class, request, pageSize)),
|
||||
null);
|
||||
} catch (MalformedURLException e) {
|
||||
throw new GHException("Malformed URL", e);
|
||||
}
|
||||
}
|
||||
|
||||
protected Iterator<GHWorkflow[]> adapt(final Iterator<GHWorkflowsPage> base) {
|
||||
return new Iterator<GHWorkflow[]>() {
|
||||
public boolean hasNext() {
|
||||
return base.hasNext();
|
||||
}
|
||||
|
||||
public GHWorkflow[] next() {
|
||||
GHWorkflowsPage v = base.next();
|
||||
if (result == null) {
|
||||
result = v;
|
||||
}
|
||||
return v.getWorkflows(owner);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
20
src/main/java/org/kohsuke/github/GHWorkflowsPage.java
Normal file
20
src/main/java/org/kohsuke/github/GHWorkflowsPage.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
/**
|
||||
* Represents the one page of workflow result when listing workflows.
|
||||
*/
|
||||
class GHWorkflowsPage {
|
||||
private int total_count;
|
||||
private GHWorkflow[] workflows;
|
||||
|
||||
public int getTotalCount() {
|
||||
return total_count;
|
||||
}
|
||||
|
||||
GHWorkflow[] getWorkflows(GHRepository owner) {
|
||||
for (GHWorkflow workflow : workflows) {
|
||||
workflow.wrapUp(owner);
|
||||
}
|
||||
return workflows;
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,8 @@ package org.kohsuke.github;
|
||||
import com.fasterxml.jackson.databind.ObjectReader;
|
||||
import com.fasterxml.jackson.databind.ObjectWriter;
|
||||
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
|
||||
import org.kohsuke.github.authorization.AuthorizationProvider;
|
||||
import org.kohsuke.github.internal.Previews;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
@@ -37,8 +39,8 @@ import java.util.logging.Logger;
|
||||
import javax.annotation.CheckForNull;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import static org.kohsuke.github.Previews.INERTIA;
|
||||
import static org.kohsuke.github.Previews.MACHINE_MAN;
|
||||
import static org.kohsuke.github.internal.Previews.INERTIA;
|
||||
import static org.kohsuke.github.internal.Previews.MACHINE_MAN;
|
||||
|
||||
/**
|
||||
* Root of the GitHub API.
|
||||
@@ -93,39 +95,112 @@ public class GitHub {
|
||||
* "http://ghe.acme.com/api/v3". Note that GitHub Enterprise has <code>/api/v3</code> in the URL. For
|
||||
* historical reasons, this parameter still accepts the bare domain name, but that's considered
|
||||
* deprecated. Password is also considered deprecated as it is no longer required for api usage.
|
||||
* @param login
|
||||
* The user ID on GitHub that you are logging in as. Can be omitted if the OAuth token is provided or if
|
||||
* logging in anonymously. Specifying this would save one API call.
|
||||
* @param oauthAccessToken
|
||||
* Secret OAuth token.
|
||||
* @param password
|
||||
* User's password. Always used in conjunction with the {@code login} parameter
|
||||
* @param connector
|
||||
* HttpConnector to use. Pass null to use default connector.
|
||||
* a connector
|
||||
* @param rateLimitHandler
|
||||
* rateLimitHandler
|
||||
* @param abuseLimitHandler
|
||||
* abuseLimitHandler
|
||||
* @param rateLimitChecker
|
||||
* rateLimitChecker
|
||||
* @param authorizationProvider
|
||||
* a authorization provider
|
||||
*/
|
||||
GitHub(String apiUrl,
|
||||
String login,
|
||||
String oauthAccessToken,
|
||||
String jwtToken,
|
||||
String password,
|
||||
HttpConnector connector,
|
||||
RateLimitHandler rateLimitHandler,
|
||||
AbuseLimitHandler abuseLimitHandler,
|
||||
GitHubRateLimitChecker rateLimitChecker) throws IOException {
|
||||
GitHubRateLimitChecker rateLimitChecker,
|
||||
AuthorizationProvider authorizationProvider) throws IOException {
|
||||
if (authorizationProvider instanceof DependentAuthorizationProvider) {
|
||||
((DependentAuthorizationProvider) authorizationProvider).bind(this);
|
||||
}
|
||||
|
||||
this.client = new GitHubHttpUrlConnectionClient(apiUrl,
|
||||
login,
|
||||
oauthAccessToken,
|
||||
jwtToken,
|
||||
password,
|
||||
connector,
|
||||
rateLimitHandler,
|
||||
abuseLimitHandler,
|
||||
rateLimitChecker,
|
||||
(myself) -> setMyself(myself));
|
||||
(myself) -> setMyself(myself),
|
||||
authorizationProvider);
|
||||
users = new ConcurrentHashMap<>();
|
||||
orgs = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
private GitHub(GitHubClient client) {
|
||||
this.client = client;
|
||||
users = new ConcurrentHashMap<>();
|
||||
orgs = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
public static abstract class DependentAuthorizationProvider implements AuthorizationProvider {
|
||||
|
||||
private GitHub baseGitHub;
|
||||
private GitHub gitHub;
|
||||
private final AuthorizationProvider authorizationProvider;
|
||||
|
||||
/**
|
||||
* An AuthorizationProvider that requires an authenticated GitHub instance to provide its authorization.
|
||||
*
|
||||
* @param authorizationProvider
|
||||
* A authorization provider to be used when refreshing this authorization provider.
|
||||
*/
|
||||
@BetaApi
|
||||
@Deprecated
|
||||
protected DependentAuthorizationProvider(AuthorizationProvider authorizationProvider) {
|
||||
this.authorizationProvider = authorizationProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds this authorization provider to a github instance.
|
||||
*
|
||||
* Only needs to be implemented by dynamic credentials providers that use a github instance in order to refresh.
|
||||
*
|
||||
* @param github
|
||||
* The github instance to be used for refreshing dynamic credentials
|
||||
*/
|
||||
synchronized void bind(GitHub github) {
|
||||
if (baseGitHub != null) {
|
||||
throw new IllegalStateException("Already bound to another GitHub instance.");
|
||||
}
|
||||
this.baseGitHub = github;
|
||||
}
|
||||
|
||||
protected synchronized final GitHub gitHub() {
|
||||
if (gitHub == null) {
|
||||
gitHub = new GitHub.AuthorizationRefreshGitHubWrapper(this.baseGitHub, authorizationProvider);
|
||||
}
|
||||
return gitHub;
|
||||
}
|
||||
}
|
||||
|
||||
private static class AuthorizationRefreshGitHubWrapper extends GitHub {
|
||||
|
||||
private final AuthorizationProvider authorizationProvider;
|
||||
|
||||
AuthorizationRefreshGitHubWrapper(GitHub github, AuthorizationProvider authorizationProvider) {
|
||||
super(github.client);
|
||||
this.authorizationProvider = authorizationProvider;
|
||||
|
||||
// no dependent authorization providers nest like this currently, but they might in future
|
||||
if (authorizationProvider instanceof DependentAuthorizationProvider) {
|
||||
((DependentAuthorizationProvider) authorizationProvider).bind(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
Requester createRequest() {
|
||||
try {
|
||||
// Override
|
||||
return super.createRequest().setHeader("Authorization", authorizationProvider.getEncodedAuthorization())
|
||||
.rateLimit(RateLimitTarget.NONE);
|
||||
} catch (IOException e) {
|
||||
throw new GHException("Failed to create requester to refresh credentials", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the credential from "~/.github" or from the System Environment Properties.
|
||||
*
|
||||
@@ -557,11 +632,28 @@ public class GitHub {
|
||||
* @return the repository by id
|
||||
* @throws IOException
|
||||
* the io exception
|
||||
*
|
||||
* @deprecated Do not use this method. It was added due to misunderstanding of the type of parameter. Use
|
||||
* {@link #getRepositoryById(long)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public GHRepository getRepositoryById(String id) throws IOException {
|
||||
return createRequest().withUrlPath("/repositories/" + id).fetch(GHRepository.class).wrap(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the repository object from its ID
|
||||
*
|
||||
* @param id
|
||||
* the id
|
||||
* @return the repository by id
|
||||
* @throws IOException
|
||||
* the io exception
|
||||
*/
|
||||
public GHRepository getRepositoryById(long id) throws IOException {
|
||||
return createRequest().withUrlPath("/repositories/" + id).fetch(GHRepository.class).wrap(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of popular open source licenses
|
||||
*
|
||||
@@ -1203,7 +1295,7 @@ public class GitHub {
|
||||
.with(new ByteArrayInputStream(text.getBytes("UTF-8")))
|
||||
.contentType("text/plain;charset=UTF-8")
|
||||
.withUrlPath("/markdown/raw")
|
||||
.fetchStream(),
|
||||
.fetchStream(Requester::copyInputStream),
|
||||
"UTF-8");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.kohsuke.github.authorization.AuthorizationProvider;
|
||||
import org.kohsuke.github.authorization.ImmutableAuthorizationProvider;
|
||||
import org.kohsuke.github.extras.ImpatientHttpConnector;
|
||||
|
||||
import java.io.File;
|
||||
@@ -22,18 +24,18 @@ import javax.annotation.Nonnull;
|
||||
*/
|
||||
public class GitHubBuilder implements Cloneable {
|
||||
|
||||
// for testing
|
||||
static File HOME_DIRECTORY = null;
|
||||
|
||||
// default scoped so unit tests can read them.
|
||||
/* private */ String endpoint = GitHubClient.GITHUB_URL;
|
||||
/* private */ String user;
|
||||
/* private */ String password;
|
||||
/* private */ String oauthToken;
|
||||
/* private */ String jwtToken;
|
||||
|
||||
private HttpConnector connector;
|
||||
|
||||
private RateLimitHandler rateLimitHandler = RateLimitHandler.WAIT;
|
||||
private AbuseLimitHandler abuseLimitHandler = AbuseLimitHandler.WAIT;
|
||||
private GitHubRateLimitChecker rateLimitChecker = new GitHubRateLimitChecker();
|
||||
/* private */ AuthorizationProvider authorizationProvider = AuthorizationProvider.ANONYMOUS;
|
||||
|
||||
/**
|
||||
* Instantiates a new Git hub builder.
|
||||
@@ -61,13 +63,13 @@ public class GitHubBuilder implements Cloneable {
|
||||
|
||||
builder = fromEnvironment();
|
||||
|
||||
if (builder.oauthToken != null || builder.user != null || builder.jwtToken != null)
|
||||
if (builder.authorizationProvider != AuthorizationProvider.ANONYMOUS)
|
||||
return builder;
|
||||
|
||||
try {
|
||||
builder = fromPropertyFile();
|
||||
|
||||
if (builder.oauthToken != null || builder.user != null || builder.jwtToken != null)
|
||||
if (builder.authorizationProvider != AuthorizationProvider.ANONYMOUS)
|
||||
return builder;
|
||||
} catch (FileNotFoundException e) {
|
||||
// fall through
|
||||
@@ -179,7 +181,7 @@ public class GitHubBuilder implements Cloneable {
|
||||
* the io exception
|
||||
*/
|
||||
public static GitHubBuilder fromPropertyFile() throws IOException {
|
||||
File homeDir = new File(System.getProperty("user.home"));
|
||||
File homeDir = HOME_DIRECTORY != null ? HOME_DIRECTORY : new File(System.getProperty("user.home"));
|
||||
File propertyFile = new File(homeDir, ".github");
|
||||
return fromPropertyFile(propertyFile.getPath());
|
||||
}
|
||||
@@ -215,9 +217,20 @@ public class GitHubBuilder implements Cloneable {
|
||||
*/
|
||||
public static GitHubBuilder fromProperties(Properties props) {
|
||||
GitHubBuilder self = new GitHubBuilder();
|
||||
self.withOAuthToken(props.getProperty("oauth"), props.getProperty("login"));
|
||||
self.withJwtToken(props.getProperty("jwt"));
|
||||
self.withPassword(props.getProperty("login"), props.getProperty("password"));
|
||||
String oauth = props.getProperty("oauth");
|
||||
String jwt = props.getProperty("jwt");
|
||||
String login = props.getProperty("login");
|
||||
String password = props.getProperty("password");
|
||||
|
||||
if (oauth != null) {
|
||||
self.withOAuthToken(oauth, login);
|
||||
}
|
||||
if (jwt != null) {
|
||||
self.withJwtToken(jwt);
|
||||
}
|
||||
if (password != null) {
|
||||
self.withPassword(login, password);
|
||||
}
|
||||
self.withEndpoint(props.getProperty("endpoint", GitHubClient.GITHUB_URL));
|
||||
return self;
|
||||
}
|
||||
@@ -247,9 +260,7 @@ public class GitHubBuilder implements Cloneable {
|
||||
* @return the git hub builder
|
||||
*/
|
||||
public GitHubBuilder withPassword(String user, String password) {
|
||||
this.user = user;
|
||||
this.password = password;
|
||||
return this;
|
||||
return withAuthorizationProvider(ImmutableAuthorizationProvider.fromLoginAndPassword(user, password));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -260,7 +271,7 @@ public class GitHubBuilder implements Cloneable {
|
||||
* @return the git hub builder
|
||||
*/
|
||||
public GitHubBuilder withOAuthToken(String oauthToken) {
|
||||
return withOAuthToken(oauthToken, null);
|
||||
return withAuthorizationProvider(ImmutableAuthorizationProvider.fromOauthToken(oauthToken));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -273,8 +284,21 @@ public class GitHubBuilder implements Cloneable {
|
||||
* @return the git hub builder
|
||||
*/
|
||||
public GitHubBuilder withOAuthToken(String oauthToken, String user) {
|
||||
this.oauthToken = oauthToken;
|
||||
this.user = user;
|
||||
return withAuthorizationProvider(ImmutableAuthorizationProvider.fromOauthToken(oauthToken, user));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures a {@link AuthorizationProvider} for this builder
|
||||
*
|
||||
* There can be only one authorization provider per client instance.
|
||||
*
|
||||
* @param authorizationProvider
|
||||
* the authorization provider
|
||||
* @return the git hub builder
|
||||
*
|
||||
*/
|
||||
public GitHubBuilder withAuthorizationProvider(final AuthorizationProvider authorizationProvider) {
|
||||
this.authorizationProvider = authorizationProvider;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -287,7 +311,7 @@ public class GitHubBuilder implements Cloneable {
|
||||
* @see GHAppInstallation#createToken(java.util.Map) GHAppInstallation#createToken(java.util.Map)
|
||||
*/
|
||||
public GitHubBuilder withAppInstallationToken(String appInstallationToken) {
|
||||
return withOAuthToken(appInstallationToken, "");
|
||||
return withAuthorizationProvider(ImmutableAuthorizationProvider.fromAppInstallationToken(appInstallationToken));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -298,8 +322,7 @@ public class GitHubBuilder implements Cloneable {
|
||||
* @return the git hub builder
|
||||
*/
|
||||
public GitHubBuilder withJwtToken(String jwtToken) {
|
||||
this.jwtToken = jwtToken;
|
||||
return this;
|
||||
return withAuthorizationProvider(ImmutableAuthorizationProvider.fromJwtToken(jwtToken));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -421,14 +444,11 @@ public class GitHubBuilder implements Cloneable {
|
||||
*/
|
||||
public GitHub build() throws IOException {
|
||||
return new GitHub(endpoint,
|
||||
user,
|
||||
oauthToken,
|
||||
jwtToken,
|
||||
password,
|
||||
connector,
|
||||
rateLimitHandler,
|
||||
abuseLimitHandler,
|
||||
rateLimitChecker);
|
||||
rateLimitChecker,
|
||||
authorizationProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,33 +1,20 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.InjectableValues;
|
||||
import com.fasterxml.jackson.databind.MapperFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.ObjectReader;
|
||||
import com.fasterxml.jackson.databind.ObjectWriter;
|
||||
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
|
||||
import com.fasterxml.jackson.databind.*;
|
||||
import com.fasterxml.jackson.databind.introspect.VisibilityChecker;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.kohsuke.github.authorization.AuthorizationProvider;
|
||||
import org.kohsuke.github.authorization.UserAuthorizationProvider;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.SocketException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.net.*;
|
||||
import java.time.Instant;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Base64;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -56,24 +43,18 @@ abstract class GitHubClient {
|
||||
static final int retryTimeoutMillis = 100;
|
||||
/* private */ final String login;
|
||||
|
||||
/**
|
||||
* Value of the authorization header to be sent with the request.
|
||||
*/
|
||||
/* private */ final String encodedAuthorization;
|
||||
|
||||
// Cache of myself object.
|
||||
private final String apiUrl;
|
||||
|
||||
protected final RateLimitHandler rateLimitHandler;
|
||||
protected final AbuseLimitHandler abuseLimitHandler;
|
||||
private final GitHubRateLimitChecker rateLimitChecker;
|
||||
private final AuthorizationProvider authorizationProvider;
|
||||
|
||||
private HttpConnector connector;
|
||||
|
||||
private final Object rateLimitLock = new Object();
|
||||
|
||||
@Nonnull
|
||||
private GHRateLimit rateLimit = GHRateLimit.DEFAULT;
|
||||
private final AtomicReference<GHRateLimit> rateLimit = new AtomicReference<>(GHRateLimit.DEFAULT);
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(GitHubClient.class.getName());
|
||||
|
||||
@@ -91,15 +72,12 @@ abstract class GitHubClient {
|
||||
}
|
||||
|
||||
GitHubClient(String apiUrl,
|
||||
String login,
|
||||
String oauthAccessToken,
|
||||
String jwtToken,
|
||||
String password,
|
||||
HttpConnector connector,
|
||||
RateLimitHandler rateLimitHandler,
|
||||
AbuseLimitHandler abuseLimitHandler,
|
||||
GitHubRateLimitChecker rateLimitChecker,
|
||||
Consumer<GHMyself> myselfConsumer) throws IOException {
|
||||
Consumer<GHMyself> myselfConsumer,
|
||||
AuthorizationProvider authorizationProvider) throws IOException {
|
||||
|
||||
if (apiUrl.endsWith("/")) {
|
||||
apiUrl = apiUrl.substring(0, apiUrl.length() - 1); // normalize
|
||||
@@ -111,33 +89,38 @@ abstract class GitHubClient {
|
||||
this.apiUrl = apiUrl;
|
||||
this.connector = connector;
|
||||
|
||||
if (oauthAccessToken != null) {
|
||||
encodedAuthorization = "token " + oauthAccessToken;
|
||||
} else {
|
||||
if (jwtToken != null) {
|
||||
encodedAuthorization = "Bearer " + jwtToken;
|
||||
} else if (password != null) {
|
||||
String authorization = (login + ':' + password);
|
||||
String charsetName = StandardCharsets.UTF_8.name();
|
||||
encodedAuthorization = "Basic "
|
||||
+ Base64.getEncoder().encodeToString(authorization.getBytes(charsetName));
|
||||
} else {// anonymous access
|
||||
encodedAuthorization = null;
|
||||
}
|
||||
}
|
||||
// Prefer credential configuration via provider
|
||||
this.authorizationProvider = authorizationProvider;
|
||||
|
||||
this.rateLimitHandler = rateLimitHandler;
|
||||
this.abuseLimitHandler = abuseLimitHandler;
|
||||
this.rateLimitChecker = rateLimitChecker;
|
||||
|
||||
if (login == null && encodedAuthorization != null && jwtToken == null) {
|
||||
GHMyself myself = fetch(GHMyself.class, "/user");
|
||||
login = myself.getLogin();
|
||||
if (myselfConsumer != null) {
|
||||
myselfConsumer.accept(myself);
|
||||
this.login = getCurrentUser(myselfConsumer);
|
||||
}
|
||||
|
||||
private String getCurrentUser(Consumer<GHMyself> myselfConsumer) throws IOException {
|
||||
String login = null;
|
||||
if (this.authorizationProvider instanceof UserAuthorizationProvider
|
||||
&& this.authorizationProvider.getEncodedAuthorization() != null) {
|
||||
|
||||
UserAuthorizationProvider userAuthorizationProvider = (UserAuthorizationProvider) this.authorizationProvider;
|
||||
|
||||
login = userAuthorizationProvider.getLogin();
|
||||
|
||||
if (login == null) {
|
||||
try {
|
||||
GHMyself myself = fetch(GHMyself.class, "/user");
|
||||
if (myselfConsumer != null) {
|
||||
myselfConsumer.accept(myself);
|
||||
}
|
||||
login = myself.getLogin();
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.login = login;
|
||||
return login;
|
||||
}
|
||||
|
||||
private <T> T fetch(Class<T> type, String urlPath) throws IOException {
|
||||
@@ -157,10 +140,9 @@ abstract class GitHubClient {
|
||||
getRateLimit();
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
if (LOGGER.isLoggable(FINE))
|
||||
LOGGER.log(FINE,
|
||||
"Exception validating credentials on " + getApiUrl() + " with login '" + login + "' " + e,
|
||||
e);
|
||||
LOGGER.log(FINE,
|
||||
"Exception validating credentials on " + getApiUrl() + " with login '" + login + "' " + e,
|
||||
e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -202,7 +184,13 @@ abstract class GitHubClient {
|
||||
* @return {@code true} if operations that require authentication will fail.
|
||||
*/
|
||||
public boolean isAnonymous() {
|
||||
return login == null && encodedAuthorization == null;
|
||||
try {
|
||||
return login == null && this.authorizationProvider.getEncodedAuthorization() == null;
|
||||
} catch (IOException e) {
|
||||
// An exception here means that the provider failed to provide authorization parameters,
|
||||
// basically meaning the same as "no auth"
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -224,6 +212,11 @@ abstract class GitHubClient {
|
||||
return getRateLimit(RateLimitTarget.NONE);
|
||||
}
|
||||
|
||||
@CheckForNull
|
||||
protected String getEncodedAuthorization() throws IOException {
|
||||
return authorizationProvider.getEncodedAuthorization();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
GHRateLimit getRateLimit(@Nonnull RateLimitTarget rateLimitTarget) throws IOException {
|
||||
GHRateLimit result;
|
||||
@@ -261,9 +254,7 @@ abstract class GitHubClient {
|
||||
@Nonnull
|
||||
@Deprecated
|
||||
GHRateLimit lastRateLimit() {
|
||||
synchronized (rateLimitLock) {
|
||||
return rateLimit;
|
||||
}
|
||||
return rateLimit.get();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -283,12 +274,19 @@ abstract class GitHubClient {
|
||||
*/
|
||||
@Nonnull
|
||||
GHRateLimit rateLimit(@Nonnull RateLimitTarget rateLimitTarget) throws IOException {
|
||||
synchronized (rateLimitLock) {
|
||||
if (rateLimit.getRecord(rateLimitTarget).isExpired()) {
|
||||
getRateLimit(rateLimitTarget);
|
||||
GHRateLimit result = rateLimit.get();
|
||||
// Most of the time rate limit is not expired, so try to avoid locking.
|
||||
if (result.getRecord(rateLimitTarget).isExpired()) {
|
||||
// if the rate limit is expired, synchronize to ensure
|
||||
// only one call to getRateLimit() is made to refresh it.
|
||||
synchronized (this) {
|
||||
if (rateLimit.get().getRecord(rateLimitTarget).isExpired()) {
|
||||
getRateLimit(rateLimitTarget);
|
||||
}
|
||||
}
|
||||
return rateLimit;
|
||||
result = rateLimit.get();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -301,15 +299,9 @@ abstract class GitHubClient {
|
||||
* {@link GHRateLimit.Record} constructed from the response header information
|
||||
*/
|
||||
private GHRateLimit updateRateLimit(@Nonnull GHRateLimit observed) {
|
||||
synchronized (rateLimitLock) {
|
||||
observed = rateLimit.getMergedRateLimit(observed);
|
||||
|
||||
if (rateLimit != observed) {
|
||||
rateLimit = observed;
|
||||
LOGGER.log(FINE, "Rate limit now: {0}", rateLimit);
|
||||
}
|
||||
return rateLimit;
|
||||
}
|
||||
GHRateLimit result = rateLimit.accumulateAndGet(observed, (current, x) -> current.getMergedRateLimit(x));
|
||||
LOGGER.log(FINEST, "Rate limit now: {0}", rateLimit.get());
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -389,12 +381,7 @@ abstract class GitHubClient {
|
||||
GitHubResponse.ResponseInfo responseInfo = null;
|
||||
try {
|
||||
try {
|
||||
if (LOGGER.isLoggable(FINE)) {
|
||||
LOGGER.log(FINE,
|
||||
"GitHub API request [" + (login == null ? "anonymous" : login) + "]: "
|
||||
+ request.method() + " " + request.url().toString());
|
||||
}
|
||||
|
||||
logRequest(request);
|
||||
rateLimitChecker.checkRateLimit(this, request);
|
||||
|
||||
responseInfo = getResponseInfo(request);
|
||||
@@ -430,6 +417,12 @@ abstract class GitHubClient {
|
||||
throw new GHIOException("Ran out of retries for URL: " + request.url().toString());
|
||||
}
|
||||
|
||||
private void logRequest(@Nonnull final GitHubRequest request) {
|
||||
LOGGER.log(FINE,
|
||||
() -> "GitHub API request [" + (login == null ? "anonymous" : login) + "]: " + request.method() + " "
|
||||
+ request.url().toString());
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
protected abstract GitHubResponse.ResponseInfo getResponseInfo(GitHubRequest request) throws IOException;
|
||||
|
||||
@@ -443,20 +436,15 @@ abstract class GitHubClient {
|
||||
// special case handling for 304 unmodified, as the content will be ""
|
||||
} else if (responseInfo.statusCode() == HttpURLConnection.HTTP_ACCEPTED) {
|
||||
|
||||
// Response code 202 means data is being generated.
|
||||
// Response code 202 means data is being generated or an action that can require some time is triggered.
|
||||
// This happens in specific cases:
|
||||
// statistics - See https://developer.github.com/v3/repos/statistics/#a-word-about-caching
|
||||
// fork creation - See https://developer.github.com/v3/repos/forks/#create-a-fork
|
||||
// workflow run cancellation - See https://docs.github.com/en/rest/reference/actions#cancel-a-workflow-run
|
||||
|
||||
if (responseInfo.url().toString().endsWith("/forks")) {
|
||||
LOGGER.log(INFO, "The fork is being created. Please try again in 5 seconds.");
|
||||
} else if (responseInfo.url().toString().endsWith("/statistics")) {
|
||||
LOGGER.log(INFO, "The statistics are being generated. Please try again in 5 seconds.");
|
||||
} else {
|
||||
LOGGER.log(INFO,
|
||||
"Received 202 from " + responseInfo.url().toString() + " . Please try again in 5 seconds.");
|
||||
}
|
||||
// Maybe throw an exception instead?
|
||||
LOGGER.log(FINE,
|
||||
"Received HTTP_ACCEPTED(202) from " + responseInfo.url().toString()
|
||||
+ " . Please try again in 5 seconds.");
|
||||
} else if (handler != null) {
|
||||
body = handler.apply(responseInfo);
|
||||
}
|
||||
@@ -568,9 +556,7 @@ abstract class GitHubClient {
|
||||
GHRateLimit.Record observed = new GHRateLimit.Record(limit, remaining, reset, responseInfo);
|
||||
updateRateLimit(GHRateLimit.fromRecord(observed, responseInfo.request().rateLimitTarget()));
|
||||
} catch (NumberFormatException | NullPointerException e) {
|
||||
if (LOGGER.isLoggable(FINEST)) {
|
||||
LOGGER.log(FINEST, "Missing or malformed X-RateLimit header: ", e);
|
||||
}
|
||||
LOGGER.log(FINEST, "Missing or malformed X-RateLimit header: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.kohsuke.github.authorization.AuthorizationProvider;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -33,25 +34,19 @@ import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||
class GitHubHttpUrlConnectionClient extends GitHubClient {
|
||||
|
||||
GitHubHttpUrlConnectionClient(String apiUrl,
|
||||
String login,
|
||||
String oauthAccessToken,
|
||||
String jwtToken,
|
||||
String password,
|
||||
HttpConnector connector,
|
||||
RateLimitHandler rateLimitHandler,
|
||||
AbuseLimitHandler abuseLimitHandler,
|
||||
GitHubRateLimitChecker rateLimitChecker,
|
||||
Consumer<GHMyself> myselfConsumer) throws IOException {
|
||||
Consumer<GHMyself> myselfConsumer,
|
||||
AuthorizationProvider authorizationProvider) throws IOException {
|
||||
super(apiUrl,
|
||||
login,
|
||||
oauthAccessToken,
|
||||
jwtToken,
|
||||
password,
|
||||
connector,
|
||||
rateLimitHandler,
|
||||
abuseLimitHandler,
|
||||
rateLimitChecker,
|
||||
myselfConsumer);
|
||||
myselfConsumer,
|
||||
authorizationProvider);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@@ -114,8 +109,12 @@ class GitHubHttpUrlConnectionClient extends GitHubClient {
|
||||
|
||||
// if the authentication is needed but no credential is given, try it anyway (so that some calls
|
||||
// that do work with anonymous access in the reduced form should still work.)
|
||||
if (client.encodedAuthorization != null)
|
||||
connection.setRequestProperty("Authorization", client.encodedAuthorization);
|
||||
if (!request.headers().containsKey("Authorization")) {
|
||||
String authorization = client.getEncodedAuthorization();
|
||||
if (authorization != null) {
|
||||
connection.setRequestProperty("Authorization", client.getEncodedAuthorization());
|
||||
}
|
||||
}
|
||||
|
||||
setRequestMethod(request.method(), connection);
|
||||
buildRequest(request, connection);
|
||||
|
||||
@@ -20,4 +20,8 @@ abstract class GitHubInteractiveObject {
|
||||
GitHubInteractiveObject(GitHub root) {
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
GitHub getRoot() {
|
||||
return root;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.kohsuke.github;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.NonNull;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.kohsuke.github.internal.Previews;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
@@ -601,13 +602,27 @@ class GitHubRequest {
|
||||
* @return the request builder
|
||||
*/
|
||||
public B set(String key, Object value) {
|
||||
for (int index = 0; index < args.size(); index++) {
|
||||
remove(key);
|
||||
return with(key, value);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all arg entries for a specific key.
|
||||
*
|
||||
* @param key
|
||||
* the key
|
||||
* @return the request builder
|
||||
*/
|
||||
public B remove(String key) {
|
||||
for (int index = 0; index < args.size();) {
|
||||
if (args.get(index).key.equals(key)) {
|
||||
args.set(index, new Entry(key, value));
|
||||
return (B) this;
|
||||
args.remove(index);
|
||||
} else {
|
||||
index++;
|
||||
}
|
||||
}
|
||||
return with(key, value);
|
||||
return (B) this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.fasterxml.jackson.core.JsonParseException;
|
||||
import com.fasterxml.jackson.databind.InjectableValues;
|
||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.kohsuke.github.function.FunctionThrows;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
@@ -194,24 +195,11 @@ class GitHubResponse<T> {
|
||||
/**
|
||||
* Represents a supplier of results that can throw.
|
||||
*
|
||||
* <p>
|
||||
* This is a <a href="package-summary.html">functional interface</a> whose functional method is
|
||||
* {@link #apply(ResponseInfo)}.
|
||||
*
|
||||
* @param <T>
|
||||
* the type of results supplied by this supplier
|
||||
*/
|
||||
@FunctionalInterface
|
||||
interface BodyHandler<T> {
|
||||
|
||||
/**
|
||||
* Gets a result.
|
||||
*
|
||||
* @return a result
|
||||
* @throws IOException
|
||||
* if an I/O Exception occurs.
|
||||
*/
|
||||
T apply(ResponseInfo input) throws IOException;
|
||||
interface BodyHandler<T> extends FunctionThrows<ResponseInfo, T, IOException> {
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import org.kohsuke.github.internal.Previews;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@@ -2,7 +2,7 @@ package org.kohsuke.github;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.kohsuke.github.Previews.SQUIRREL_GIRL;
|
||||
import static org.kohsuke.github.internal.Previews.SQUIRREL_GIRL;
|
||||
|
||||
/**
|
||||
* Those {@link GHObject}s that can have {@linkplain GHReaction reactions}.
|
||||
|
||||
@@ -23,7 +23,9 @@
|
||||
*/
|
||||
package org.kohsuke.github;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.NonNull;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.kohsuke.github.function.InputStreamFunction;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
@@ -106,15 +108,31 @@ class Requester extends GitHubRequest.Builder<Requester> {
|
||||
* Response input stream. There are scenarios where direct stream reading is needed, however it is better to use
|
||||
* {@link #fetch(Class)} where possible.
|
||||
*
|
||||
* @return the input stream
|
||||
* @throws IOException
|
||||
* the io exception
|
||||
*/
|
||||
public InputStream fetchStream() throws IOException {
|
||||
return client
|
||||
.sendRequest(this,
|
||||
(responseInfo) -> new ByteArrayInputStream(IOUtils.toByteArray(responseInfo.bodyStream())))
|
||||
.body();
|
||||
public <T> T fetchStream(@Nonnull InputStreamFunction<T> handler) throws IOException {
|
||||
return client.sendRequest(this, (responseInfo) -> handler.apply(responseInfo.bodyStream())).body();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to make it easy to pull streams.
|
||||
*
|
||||
* Copies an input stream to an in-memory input stream. The performance on this is not great but
|
||||
* {@link GitHubResponse.ResponseInfo#bodyStream()} is closed at the end of every call to
|
||||
* {@link GitHubClient#sendRequest(GitHubRequest, GitHubResponse.BodyHandler)}, so any reads to the original input
|
||||
* stream must be completed before then. There are a number of deprecated methods that return {@link InputStream}.
|
||||
* This method keeps all of them using the same code path.
|
||||
*
|
||||
* @param inputStream
|
||||
* the input stream to be copied
|
||||
* @return an in-memory copy of the passed input stream
|
||||
* @throws IOException
|
||||
* if an error occurs while copying the stream
|
||||
*/
|
||||
@NonNull
|
||||
public static InputStream copyInputStream(InputStream inputStream) throws IOException {
|
||||
return new ByteArrayInputStream(IOUtils.toByteArray(inputStream));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package org.kohsuke.github.authorization;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Provides a functional interface that returns a valid encodedAuthorization. This strategy allows for a provider that
|
||||
* dynamically changes the credentials. Each request will request the credentials from the provider.
|
||||
*/
|
||||
public interface AuthorizationProvider {
|
||||
/**
|
||||
* An static instance for an ANONYMOUS authorization provider
|
||||
*/
|
||||
AuthorizationProvider ANONYMOUS = new AnonymousAuthorizationProvider();
|
||||
|
||||
/**
|
||||
* Returns the credentials to be used with a given request. As an example, a authorization provider for a bearer
|
||||
* token will return something like:
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
* @Override
|
||||
* public String getEncodedAuthorization() {
|
||||
* return "Bearer myBearerToken";
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @return encoded authorization string, can be null
|
||||
* @throws IOException
|
||||
* on any error that prevents the provider from getting a valid authorization
|
||||
*/
|
||||
String getEncodedAuthorization() throws IOException;
|
||||
|
||||
/**
|
||||
* A {@link AuthorizationProvider} that ensures that no credentials are returned
|
||||
*/
|
||||
class AnonymousAuthorizationProvider implements AuthorizationProvider {
|
||||
@Override
|
||||
public String getEncodedAuthorization() throws IOException {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package org.kohsuke.github.authorization;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
|
||||
import javax.annotation.CheckForNull;
|
||||
|
||||
/**
|
||||
* A {@link AuthorizationProvider} that always returns the same credentials
|
||||
*/
|
||||
public class ImmutableAuthorizationProvider implements AuthorizationProvider {
|
||||
|
||||
private final String authorization;
|
||||
|
||||
public ImmutableAuthorizationProvider(String authorization) {
|
||||
this.authorization = authorization;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns a {@link AuthorizationProvider} from a given oauthAccessToken
|
||||
*
|
||||
* @param oauthAccessToken
|
||||
* The token
|
||||
* @return a correctly configured {@link AuthorizationProvider} that will always return the same provided
|
||||
* oauthAccessToken
|
||||
*/
|
||||
public static AuthorizationProvider fromOauthToken(String oauthAccessToken) {
|
||||
return new UserProvider(String.format("token %s", oauthAccessToken));
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns a {@link AuthorizationProvider} from a given oauthAccessToken
|
||||
*
|
||||
* @param oauthAccessToken
|
||||
* The token
|
||||
* @param login
|
||||
* The login for this token
|
||||
*
|
||||
* @return a correctly configured {@link AuthorizationProvider} that will always return the same provided
|
||||
* oauthAccessToken
|
||||
*/
|
||||
public static AuthorizationProvider fromOauthToken(String oauthAccessToken, String login) {
|
||||
return new UserProvider(String.format("token %s", oauthAccessToken), login);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns a {@link AuthorizationProvider} from a given App Installation Token
|
||||
*
|
||||
* @param appInstallationToken
|
||||
* A string containing the GitHub App installation token
|
||||
* @return the configured Builder from given GitHub App installation token.
|
||||
*/
|
||||
public static AuthorizationProvider fromAppInstallationToken(String appInstallationToken) {
|
||||
return fromOauthToken(appInstallationToken, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns a {@link AuthorizationProvider} from a given jwtToken
|
||||
*
|
||||
* @param jwtToken
|
||||
* The JWT token
|
||||
* @return a correctly configured {@link AuthorizationProvider} that will always return the same provided jwtToken
|
||||
*/
|
||||
public static AuthorizationProvider fromJwtToken(String jwtToken) {
|
||||
return new ImmutableAuthorizationProvider(String.format("Bearer %s", jwtToken));
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns a {@link AuthorizationProvider} from the given user/password pair
|
||||
*
|
||||
* @param login
|
||||
* The login for the user, usually the same as the username
|
||||
* @param password
|
||||
* The password for the associated user
|
||||
* @return a correctly configured {@link AuthorizationProvider} that will always return the credentials for the same
|
||||
* user and password combo
|
||||
* @deprecated Login with password credentials are no longer supported by GitHub
|
||||
*/
|
||||
@Deprecated
|
||||
public static AuthorizationProvider fromLoginAndPassword(String login, String password) {
|
||||
try {
|
||||
String authorization = (String.format("%s:%s", login, password));
|
||||
String charsetName = StandardCharsets.UTF_8.name();
|
||||
String b64encoded = Base64.getEncoder().encodeToString(authorization.getBytes(charsetName));
|
||||
String encodedAuthorization = String.format("Basic %s", b64encoded);
|
||||
return new UserProvider(encodedAuthorization, login);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// If UTF-8 isn't supported, there are bigger problems
|
||||
throw new IllegalStateException("Could not generate encoded authorization", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEncodedAuthorization() {
|
||||
return this.authorization;
|
||||
}
|
||||
|
||||
/**
|
||||
* An internal class representing all user-related credentials, which are credentials that have a login or should
|
||||
* query the user endpoint for the login matching this credential.
|
||||
*/
|
||||
private static class UserProvider extends ImmutableAuthorizationProvider implements UserAuthorizationProvider {
|
||||
|
||||
private final String login;
|
||||
|
||||
UserProvider(String authorization) {
|
||||
this(authorization, null);
|
||||
}
|
||||
|
||||
UserProvider(String authorization, String login) {
|
||||
super(authorization);
|
||||
this.login = login;
|
||||
}
|
||||
|
||||
@CheckForNull
|
||||
@Override
|
||||
public String getLogin() {
|
||||
return login;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package org.kohsuke.github.authorization;
|
||||
|
||||
import org.kohsuke.github.BetaApi;
|
||||
import org.kohsuke.github.GHAppInstallation;
|
||||
import org.kohsuke.github.GHAppInstallationToken;
|
||||
import org.kohsuke.github.GitHub;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Provides an AuthorizationProvider that performs automatic token refresh.
|
||||
*/
|
||||
public class OrgAppInstallationAuthorizationProvider extends GitHub.DependentAuthorizationProvider {
|
||||
|
||||
private final String organizationName;
|
||||
|
||||
private String latestToken;
|
||||
|
||||
@Nonnull
|
||||
private Instant validUntil = Instant.MIN;
|
||||
|
||||
/**
|
||||
* Provides an AuthorizationProvider that performs automatic token refresh, based on an previously authenticated
|
||||
* github client.
|
||||
*
|
||||
* @param organizationName
|
||||
* The name of the organization where the application is installed
|
||||
* @param authorizationProvider
|
||||
* A authorization provider that returns a JWT token that can be used to refresh the App Installation
|
||||
* token from GitHub.
|
||||
*/
|
||||
@BetaApi
|
||||
@Deprecated
|
||||
public OrgAppInstallationAuthorizationProvider(String organizationName,
|
||||
AuthorizationProvider authorizationProvider) {
|
||||
super(authorizationProvider);
|
||||
this.organizationName = organizationName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEncodedAuthorization() throws IOException {
|
||||
synchronized (this) {
|
||||
if (latestToken == null || Instant.now().isAfter(this.validUntil)) {
|
||||
refreshToken();
|
||||
}
|
||||
return String.format("token %s", latestToken);
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshToken() throws IOException {
|
||||
GitHub gitHub = this.gitHub();
|
||||
GHAppInstallation installationByOrganization = gitHub.getApp()
|
||||
.getInstallationByOrganization(this.organizationName);
|
||||
GHAppInstallationToken ghAppInstallationToken = installationByOrganization.createToken().create();
|
||||
this.validUntil = ghAppInstallationToken.getExpiresAt().toInstant().minus(Duration.ofMinutes(5));
|
||||
this.latestToken = Objects.requireNonNull(ghAppInstallationToken.getToken());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.kohsuke.github.authorization;
|
||||
|
||||
import javax.annotation.CheckForNull;
|
||||
|
||||
/**
|
||||
* Interface for all user-related authorization providers.
|
||||
*
|
||||
* {@link AuthorizationProvider}s can apply to a number of different account types. This interface applies to providers
|
||||
* for user accounts, ones that have a login or should query the "/user" endpoint for the login matching this
|
||||
* credential.
|
||||
*/
|
||||
public interface UserAuthorizationProvider extends AuthorizationProvider {
|
||||
|
||||
/**
|
||||
* Gets the user login name.
|
||||
*
|
||||
* @return the user login for this provider, or {@code null} if the login value should be queried from the "/user"
|
||||
* endpoint.
|
||||
*/
|
||||
@CheckForNull
|
||||
String getLogin();
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package org.kohsuke.github.extras.authorization;
|
||||
|
||||
import io.jsonwebtoken.JwtBuilder;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import org.kohsuke.github.authorization.AuthorizationProvider;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Base64;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* A authorization provider that gives valid JWT tokens. These tokens are then used to create a time-based token to
|
||||
* authenticate as an application. This token provider does not provide any kind of caching, and will always request a
|
||||
* new token to the API.
|
||||
*/
|
||||
public class JWTTokenProvider implements AuthorizationProvider {
|
||||
|
||||
private final PrivateKey privateKey;
|
||||
|
||||
@Nonnull
|
||||
private Instant validUntil = Instant.MIN;
|
||||
|
||||
private String token;
|
||||
|
||||
/**
|
||||
* The identifier for the application
|
||||
*/
|
||||
private final String applicationId;
|
||||
|
||||
public JWTTokenProvider(String applicationId, File keyFile) throws GeneralSecurityException, IOException {
|
||||
this(applicationId, keyFile.toPath());
|
||||
}
|
||||
|
||||
public JWTTokenProvider(String applicationId, Path keyPath) throws GeneralSecurityException, IOException {
|
||||
this(applicationId, new String(Files.readAllBytes(keyPath), StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
public JWTTokenProvider(String applicationId, String keyString) throws GeneralSecurityException {
|
||||
this(applicationId, getPrivateKeyFromString(keyString));
|
||||
}
|
||||
|
||||
public JWTTokenProvider(String applicationId, PrivateKey privateKey) {
|
||||
this.privateKey = privateKey;
|
||||
this.applicationId = applicationId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEncodedAuthorization() throws IOException {
|
||||
synchronized (this) {
|
||||
if (Instant.now().isAfter(validUntil)) {
|
||||
token = refreshJWT();
|
||||
}
|
||||
return String.format("Bearer %s", token);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a PKCS#8 formatted private key in string format into a java PrivateKey
|
||||
*
|
||||
* @param key
|
||||
* PCKS#8 string
|
||||
* @return private key
|
||||
* @throws GeneralSecurityException
|
||||
* if we couldn't parse the string
|
||||
*/
|
||||
private static PrivateKey getPrivateKeyFromString(final String key) throws GeneralSecurityException {
|
||||
if (key.contains(" RSA ")) {
|
||||
throw new InvalidKeySpecException(
|
||||
"Private key must be a PKCS#8 formatted string, to convert it from PKCS#1 use: "
|
||||
+ "openssl pkcs8 -topk8 -inform PEM -outform PEM -in current-key.pem -out new-key.pem -nocrypt");
|
||||
}
|
||||
|
||||
// Remove all comments and whitespace from PEM
|
||||
// such as "-----BEGIN PRIVATE KEY-----" and newlines
|
||||
String privateKeyContent = key.replaceAll("(?m)^--.*", "").replaceAll("\\s", "");
|
||||
|
||||
KeyFactory kf = KeyFactory.getInstance("RSA");
|
||||
|
||||
try {
|
||||
byte[] decode = Base64.getDecoder().decode(privateKeyContent);
|
||||
PKCS8EncodedKeySpec keySpecPKCS8 = new PKCS8EncodedKeySpec(decode);
|
||||
|
||||
return kf.generatePrivate(keySpecPKCS8);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new InvalidKeySpecException("Failed to decode private key: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private String refreshJWT() {
|
||||
Instant now = Instant.now();
|
||||
|
||||
// Max token expiration is 10 minutes for GitHub
|
||||
// We use a smaller window since we likely will not need more than a few seconds
|
||||
Instant expiration = now.plus(Duration.ofMinutes(8));
|
||||
|
||||
// Setting the issued at to a time in the past to allow for clock skew
|
||||
Instant issuedAt = getIssuedAt(now);
|
||||
|
||||
// Let's set the JWT Claims
|
||||
JwtBuilder builder = Jwts.builder()
|
||||
.setIssuedAt(Date.from(issuedAt))
|
||||
.setExpiration(Date.from(expiration))
|
||||
.setIssuer(this.applicationId)
|
||||
.signWith(privateKey, SignatureAlgorithm.RS256);
|
||||
|
||||
// Token will refresh 2 minutes before it expires
|
||||
validUntil = expiration.minus(Duration.ofMinutes(2));
|
||||
|
||||
// Builds the JWT and serializes it to a compact, URL-safe string
|
||||
return builder.compact();
|
||||
}
|
||||
|
||||
Instant getIssuedAt(Instant now) {
|
||||
return now.minus(Duration.ofMinutes(2));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.kohsuke.github.function;
|
||||
|
||||
/**
|
||||
* A functional interface, equivalent to {@link java.util.function.Function} but that allows throwing {@link Throwable}
|
||||
*
|
||||
* @param <T>
|
||||
* the type of input
|
||||
* @param <R>
|
||||
* the type of output
|
||||
* @param <E>
|
||||
* the type of error
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface FunctionThrows<T, R, E extends Throwable> {
|
||||
/**
|
||||
* Apply r.
|
||||
*
|
||||
* @param input
|
||||
* the input
|
||||
* @return the r
|
||||
* @throws E
|
||||
* the e
|
||||
*/
|
||||
R apply(T input) throws E;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package org.kohsuke.github.function;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* A functional interface, equivalent to {@link java.util.function.Function} but that allows throwing {@link Throwable}
|
||||
*
|
||||
* @param <R>
|
||||
* the type to of object to be returned
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface InputStreamFunction<R> extends FunctionThrows<InputStream, R, IOException> {
|
||||
}
|
||||
62
src/main/java/org/kohsuke/github/internal/EnumUtils.java
Normal file
62
src/main/java/org/kohsuke/github/internal/EnumUtils.java
Normal file
@@ -0,0 +1,62 @@
|
||||
package org.kohsuke.github.internal;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Utils for Enums.
|
||||
*/
|
||||
public final class EnumUtils {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(EnumUtils.class.getName());
|
||||
|
||||
/**
|
||||
* Returns an enum value matching the value if found, null if the value is null and {@code defaultEnum} if the value
|
||||
* cannot be matched to a value of the enum.
|
||||
* <p>
|
||||
* The value is converted to uppercase before being matched to the enum values.
|
||||
*
|
||||
* @param <E>
|
||||
* the type of the enum
|
||||
* @param enumClass
|
||||
* the type of the enum
|
||||
* @param value
|
||||
* the value to interpret
|
||||
* @param defaultEnum
|
||||
* the default enum value if the value doesn't match one of the enum value
|
||||
* @return an enum value or null
|
||||
*/
|
||||
public static <E extends Enum<E>> E getNullableEnumOrDefault(Class<E> enumClass, String value, E defaultEnum) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
return getEnumOrDefault(enumClass, value, defaultEnum);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an enum value matching the value if found, {@code defaultEnum} if the value is null or cannot be matched
|
||||
* to a value of the enum.
|
||||
*
|
||||
* @param <E>
|
||||
* the type of the enum
|
||||
* @param enumClass
|
||||
* the type of the enum
|
||||
* @param value
|
||||
* the value to interpret
|
||||
* @param defaultEnum
|
||||
* the default enum value if the value doesn't match one of the enum value
|
||||
* @return an enum value
|
||||
*/
|
||||
public static <E extends Enum<E>> E getEnumOrDefault(Class<E> enumClass, String value, E defaultEnum) {
|
||||
try {
|
||||
return Enum.valueOf(enumClass, value.toUpperCase(Locale.ROOT));
|
||||
} catch (NullPointerException | IllegalArgumentException e) {
|
||||
LOGGER.warning("Unknown value " + value + " for enum class " + enumClass.getName() + ", defaulting to "
|
||||
+ defaultEnum.name());
|
||||
return defaultEnum;
|
||||
}
|
||||
}
|
||||
|
||||
private EnumUtils() {
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.kohsuke.github;
|
||||
package org.kohsuke.github.internal;
|
||||
|
||||
/**
|
||||
* Provides the media type strings for GitHub API previews
|
||||
@@ -7,7 +7,7 @@ package org.kohsuke.github;
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
enum Previews {
|
||||
public enum Previews {
|
||||
|
||||
/**
|
||||
* Check-runs and check-suites
|
||||
@@ -2,8 +2,14 @@ package org.kohsuke.github;
|
||||
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.kohsuke.github.authorization.AuthorizationProvider;
|
||||
import org.kohsuke.github.extras.authorization.JWTTokenProvider;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
@@ -21,6 +27,26 @@ public class AbstractGHAppInstallationTest extends AbstractGitHubWireMockTest {
|
||||
private static String PRIVATE_KEY_FILE_APP_2 = "/ghapi-test-app-2.private-key.pem";
|
||||
private static String PRIVATE_KEY_FILE_APP_3 = "/ghapi-test-app-3.private-key.pem";
|
||||
|
||||
private static AuthorizationProvider JWT_PROVIDER_1;
|
||||
private static AuthorizationProvider JWT_PROVIDER_2;
|
||||
private static AuthorizationProvider JWT_PROVIDER_3;
|
||||
|
||||
AbstractGHAppInstallationTest() {
|
||||
try {
|
||||
JWT_PROVIDER_1 = new JWTTokenProvider(TEST_APP_ID_1,
|
||||
new File(this.getClass().getResource(PRIVATE_KEY_FILE_APP_1).getFile()));
|
||||
JWT_PROVIDER_2 = new JWTTokenProvider(TEST_APP_ID_2,
|
||||
new File(this.getClass().getResource(PRIVATE_KEY_FILE_APP_2).getFile()).toPath());
|
||||
JWT_PROVIDER_3 = new JWTTokenProvider(TEST_APP_ID_3,
|
||||
new String(
|
||||
Files.readAllBytes(
|
||||
new File(this.getClass().getResource(PRIVATE_KEY_FILE_APP_3).getFile()).toPath()),
|
||||
StandardCharsets.UTF_8));
|
||||
} catch (GeneralSecurityException | IOException e) {
|
||||
throw new RuntimeException("These should never fail", e);
|
||||
}
|
||||
}
|
||||
|
||||
private String createJwtToken(String keyFileResouceName, String appId) {
|
||||
try {
|
||||
String keyPEM = IOUtils.toString(this.getClass().getResource(keyFileResouceName), "US-ASCII")
|
||||
@@ -54,24 +80,25 @@ public class AbstractGHAppInstallationTest extends AbstractGitHubWireMockTest {
|
||||
.findFirst()
|
||||
.get();
|
||||
|
||||
appInstallation
|
||||
.setRoot(getGitHubBuilder().withAppInstallationToken(appInstallation.createToken().create().getToken())
|
||||
.withEndpoint(mockGitHub.apiServer().baseUrl())
|
||||
.build());
|
||||
// TODO: this is odd
|
||||
// appInstallation
|
||||
// .setRoot(getGitHubBuilder().withAppInstallationToken(appInstallation.createToken().create().getToken())
|
||||
// .withEndpoint(mockGitHub.apiServer().baseUrl())
|
||||
// .build());
|
||||
|
||||
return appInstallation;
|
||||
}
|
||||
|
||||
protected GHAppInstallation getAppInstallationWithTokenApp1() throws IOException {
|
||||
return getAppInstallationWithToken(createJwtToken(PRIVATE_KEY_FILE_APP_1, TEST_APP_ID_1));
|
||||
return getAppInstallationWithToken(JWT_PROVIDER_1.getEncodedAuthorization());
|
||||
}
|
||||
|
||||
protected GHAppInstallation getAppInstallationWithTokenApp2() throws IOException {
|
||||
return getAppInstallationWithToken(createJwtToken(PRIVATE_KEY_FILE_APP_2, TEST_APP_ID_2));
|
||||
return getAppInstallationWithToken(JWT_PROVIDER_2.getEncodedAuthorization());
|
||||
}
|
||||
|
||||
protected GHAppInstallation getAppInstallationWithTokenApp3() throws IOException {
|
||||
return getAppInstallationWithToken(createJwtToken(PRIVATE_KEY_FILE_APP_3, TEST_APP_ID_3));
|
||||
return getAppInstallationWithToken(JWT_PROVIDER_3.getEncodedAuthorization());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,10 +4,9 @@ import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
|
||||
import com.github.tomakehurst.wiremock.extension.responsetemplating.ResponseTemplateTransformer;
|
||||
import com.github.tomakehurst.wiremock.extension.responsetemplating.helpers.HandlebarsCurrentDateHelper;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.hamcrest.Description;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.hamcrest.MatcherAssert;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.hamcrest.StringDescription;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
@@ -21,13 +20,14 @@ import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assume.assumeFalse;
|
||||
import static org.junit.Assume.assumeTrue;
|
||||
|
||||
/**
|
||||
* @author Liam Newman
|
||||
*/
|
||||
public abstract class AbstractGitHubWireMockTest extends Assert {
|
||||
public abstract class AbstractGitHubWireMockTest {
|
||||
|
||||
private final GitHubBuilder githubBuilder = createGitHubBuilder();
|
||||
|
||||
@@ -45,7 +45,7 @@ public abstract class AbstractGitHubWireMockTest extends Assert {
|
||||
*/
|
||||
protected GitHub gitHub;
|
||||
|
||||
private GitHub gitHubBeforeAfter;
|
||||
private GitHub nonRecordingGitHub;
|
||||
|
||||
protected final String baseFilesClassPath = this.getClass().getName().replace('.', '/');
|
||||
protected final String baseRecordPath = "src/test/resources/" + baseFilesClassPath + "/wiremock";
|
||||
@@ -100,7 +100,6 @@ public abstract class AbstractGitHubWireMockTest extends Assert {
|
||||
// This sets the user and password to a placeholder for wiremock testing
|
||||
// This makes the tests believe they are running with permissions
|
||||
// The recorded stubs will behave like they running with permissions
|
||||
builder.oauthToken = null;
|
||||
builder.withPassword(STUBBED_USER_LOGIN, STUBBED_USER_PASSWORD);
|
||||
}
|
||||
|
||||
@@ -116,9 +115,9 @@ public abstract class AbstractGitHubWireMockTest extends Assert {
|
||||
}
|
||||
|
||||
if (mockGitHub.isUseProxy()) {
|
||||
gitHubBeforeAfter = getGitHubBuilder().withEndpoint("https://api.github.com/").build();
|
||||
nonRecordingGitHub = getGitHubBuilder().withEndpoint("https://api.github.com/").build();
|
||||
} else {
|
||||
gitHubBeforeAfter = null;
|
||||
nonRecordingGitHub = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,7 +210,7 @@ public abstract class AbstractGitHubWireMockTest extends Assert {
|
||||
if (mockGitHub.isUseProxy()) {
|
||||
tempGitHubRepositories.add(fullName);
|
||||
try {
|
||||
GHRepository repository = getGitHubBeforeAfter().getRepository(fullName);
|
||||
GHRepository repository = getNonRecordingGitHub().getRepository(fullName);
|
||||
if (repository != null) {
|
||||
repository.delete();
|
||||
}
|
||||
@@ -228,22 +227,22 @@ public abstract class AbstractGitHubWireMockTest extends Assert {
|
||||
*
|
||||
* @return a github instance after checking Authentication
|
||||
*/
|
||||
public GitHub getGitHubBeforeAfter() {
|
||||
verifyAuthenticated(gitHubBeforeAfter);
|
||||
return gitHubBeforeAfter;
|
||||
public GitHub getNonRecordingGitHub() {
|
||||
verifyAuthenticated(nonRecordingGitHub);
|
||||
return nonRecordingGitHub;
|
||||
}
|
||||
|
||||
protected void kohsuke() {
|
||||
// No-op for now
|
||||
// Generally this means the test is doing something that requires additional access rights
|
||||
// Not always clear which ones.
|
||||
// TODO: Add helpers that assert the expected rights using gitHubBeforeAfter and only when proxy is enabled
|
||||
// TODO: Add helpers that assert the expected rights using nonRecordingGitHub and only when proxy is enabled
|
||||
// String login = getUserTest().getLogin();
|
||||
// assumeTrue(login.equals("kohsuke") || login.equals("kohsuke2"));
|
||||
}
|
||||
|
||||
private GHCreateRepositoryBuilder getCreateBuilder(String name) throws IOException {
|
||||
GitHub github = getGitHubBeforeAfter();
|
||||
GitHub github = getNonRecordingGitHub();
|
||||
|
||||
if (mockGitHub.isTestWithOrg()) {
|
||||
return github.getOrganization(GITHUB_API_TEST_ORG).createRepository(name);
|
||||
@@ -256,52 +255,23 @@ public abstract class AbstractGitHubWireMockTest extends Assert {
|
||||
return mockGitHub.isTestWithOrg() ? GITHUB_API_TEST_ORG : gitHub.getMyself().getLogin();
|
||||
}
|
||||
|
||||
public static void fail() {
|
||||
Assert.fail();
|
||||
}
|
||||
public static void fail(String reason) {
|
||||
Assert.fail(reason);
|
||||
}
|
||||
|
||||
public static <T> void assertThat(T actual, Matcher<? super T> matcher) {
|
||||
assertThat("", actual, matcher);
|
||||
MatcherAssert.assertThat("", actual, matcher);
|
||||
}
|
||||
|
||||
public static <T> void assertThat(String reason, T actual, Matcher<? super T> matcher) {
|
||||
if (!matcher.matches(actual)) {
|
||||
Description description = new StringDescription();
|
||||
description.appendText(reason)
|
||||
.appendText(System.lineSeparator())
|
||||
.appendText("Expected: ")
|
||||
.appendDescriptionOf(matcher)
|
||||
.appendText(System.lineSeparator())
|
||||
.appendText(" but: ");
|
||||
matcher.describeMismatch(actual, description);
|
||||
throw new AssertionError(description.toString());
|
||||
}
|
||||
MatcherAssert.assertThat(reason, actual, matcher);
|
||||
}
|
||||
|
||||
public static void assertThat(String reason, boolean assertion) {
|
||||
if (!assertion) {
|
||||
throw new AssertionError(reason);
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertEquals(Object expected, Object actual) {
|
||||
assertThat(actual, Matchers.equalTo(expected));
|
||||
}
|
||||
|
||||
public static void assertNotEquals(Object expected, Object actual) {
|
||||
assertThat(actual, Matchers.not(expected));
|
||||
}
|
||||
|
||||
public static void assertNotNull(Object actual) {
|
||||
assertThat(actual, Matchers.notNullValue());
|
||||
}
|
||||
|
||||
public static void assertNull(Object actual) {
|
||||
assertThat(actual, Matchers.nullValue());
|
||||
}
|
||||
|
||||
public static void assertTrue(Boolean condition) {
|
||||
assertThat(condition, Matchers.is(true));
|
||||
}
|
||||
|
||||
public static void assertFalse(Boolean condition) {
|
||||
assertThat(condition, Matchers.is(false));
|
||||
MatcherAssert.assertThat(reason, assertion);
|
||||
}
|
||||
|
||||
protected static class TemplatingHelper {
|
||||
|
||||
@@ -17,12 +17,8 @@ import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.CoreMatchers.sameInstance;
|
||||
import static org.hamcrest.Matchers.hasProperty;
|
||||
import static org.hamcrest.Matchers.oneOf;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
/**
|
||||
* Unit test for simple App.
|
||||
@@ -37,10 +33,11 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
cleanupUserRepository("github-api-test-rename");
|
||||
cleanupUserRepository(targetName);
|
||||
|
||||
GHRepository r = gitHub.createRepository("github-api-test-rename",
|
||||
"a test repository",
|
||||
"http://github-api.kohsuke.org/",
|
||||
true);
|
||||
GHRepository r = gitHub.createRepository("github-api-test-rename")
|
||||
.description("a test repository")
|
||||
.homepage("http://github-api.kohsuke.org/")
|
||||
.private_(false)
|
||||
.create();
|
||||
|
||||
assertThat(r.hasIssues(), is(true));
|
||||
assertThat(r.hasWiki(), is(true));
|
||||
@@ -86,20 +83,20 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
if (mockGitHub.isUseProxy()) {
|
||||
Thread.sleep(3000);
|
||||
}
|
||||
assertNotNull(r.getReadme());
|
||||
assertThat(r.getReadme(), notNullValue());
|
||||
|
||||
r.delete();
|
||||
}
|
||||
|
||||
private void cleanupUserRepository(final String name) throws IOException {
|
||||
if (mockGitHub.isUseProxy()) {
|
||||
cleanupRepository(getUser(getGitHubBeforeAfter()).getLogin() + "/" + name);
|
||||
cleanupRepository(getUser(getNonRecordingGitHub()).getLogin() + "/" + name);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCredentialValid() throws IOException {
|
||||
assertTrue(gitHub.isCredentialValid());
|
||||
assertThat(gitHub.isCredentialValid(), is(true));
|
||||
assertThat(gitHub.lastRateLimit().getCore(), not(instanceOf(GHRateLimit.UnknownLimitRecord.class)));
|
||||
assertThat(gitHub.lastRateLimit().getCore().getLimit(), equalTo(5000));
|
||||
|
||||
@@ -107,7 +104,7 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
.withEndpoint(mockGitHub.apiServer().baseUrl())
|
||||
.build();
|
||||
assertThat(gitHub.lastRateLimit(), sameInstance(GHRateLimit.DEFAULT));
|
||||
assertFalse(gitHub.isCredentialValid());
|
||||
assertThat(gitHub.isCredentialValid(), is(false));
|
||||
// For invalid credentials, we get a 401 but it includes anonymous rate limit headers
|
||||
assertThat(gitHub.lastRateLimit().getCore(), not(instanceOf(GHRateLimit.UnknownLimitRecord.class)));
|
||||
assertThat(gitHub.lastRateLimit().getCore().getLimit(), equalTo(60));
|
||||
@@ -118,7 +115,7 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
// Simulated GHE: getRateLimit returns 404
|
||||
assertThat(gitHub.lastRateLimit(), sameInstance(GHRateLimit.DEFAULT));
|
||||
assertThat(gitHub.lastRateLimit().getCore().isExpired(), is(true));
|
||||
assertTrue(gitHub.isCredentialValid());
|
||||
assertThat(gitHub.isCredentialValid(), is(true));
|
||||
|
||||
// lastRateLimitUpdates because 404 still includes header rate limit info
|
||||
assertThat(gitHub.lastRateLimit(), notNullValue());
|
||||
@@ -129,7 +126,7 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
.withEndpoint(mockGitHub.apiServer().baseUrl())
|
||||
.build();
|
||||
assertThat(gitHub.lastRateLimit(), sameInstance(GHRateLimit.DEFAULT));
|
||||
assertFalse(gitHub.isCredentialValid());
|
||||
assertThat(gitHub.isCredentialValid(), is(false));
|
||||
// Simulated GHE: For invalid credentials, we get a 401 that does not include ratelimit info
|
||||
assertThat(gitHub.lastRateLimit(), sameInstance(GHRateLimit.DEFAULT));
|
||||
}
|
||||
@@ -137,13 +134,36 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
@Test
|
||||
public void testIssueWithNoComment() throws IOException {
|
||||
GHRepository repository = gitHub.getRepository("kohsuke/test");
|
||||
List<GHIssueComment> v = repository.getIssue(4).getComments();
|
||||
GHIssue i = repository.getIssue(4);
|
||||
List<GHIssueComment> v = i.getComments();
|
||||
// System.out.println(v);
|
||||
assertTrue(v.isEmpty());
|
||||
assertThat(v, is(empty()));
|
||||
|
||||
v = repository.getIssue(3).getComments();
|
||||
i = repository.getIssue(3);
|
||||
v = i.getComments();
|
||||
// System.out.println(v);
|
||||
assertTrue(v.size() == 3);
|
||||
assertThat(v.size(), equalTo(3));
|
||||
assertThat(v.get(0).getHtmlUrl().toString(),
|
||||
equalTo("https://github.com/kohsuke/test/issues/3#issuecomment-8547249"));
|
||||
assertThat(v.get(0).getUrl().toString(), endsWith("/repos/kohsuke/test/issues/comments/8547249"));
|
||||
assertThat(v.get(0).getNodeId(), equalTo("MDEyOklzc3VlQ29tbWVudDg1NDcyNDk="));
|
||||
assertThat(v.get(0).getParent().getNumber(), equalTo(3));
|
||||
assertThat(v.get(0).getParent().getId(), equalTo(6863845L));
|
||||
assertThat(v.get(0).getUser().getLogin(), equalTo("kohsuke"));
|
||||
assertThat(v.get(0).listReactions().toList(), is(empty()));
|
||||
|
||||
assertThat(v.get(1).getHtmlUrl().toString(),
|
||||
equalTo("https://github.com/kohsuke/test/issues/3#issuecomment-8547251"));
|
||||
assertThat(v.get(1).getUrl().toString(), endsWith("/repos/kohsuke/test/issues/comments/8547251"));
|
||||
assertThat(v.get(1).getNodeId(), equalTo("MDEyOklzc3VlQ29tbWVudDg1NDcyNTE="));
|
||||
assertThat(v.get(1).getParent().getNumber(), equalTo(3));
|
||||
assertThat(v.get(1).getUser().getLogin(), equalTo("kohsuke"));
|
||||
List<GHReaction> reactions = v.get(1).listReactions().toList();
|
||||
assertThat(reactions.size(), equalTo(3));
|
||||
|
||||
// TODO: Add comment CRUD test
|
||||
// TODO: Add reactions CRUD test
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -158,56 +178,67 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
.label("question")
|
||||
.milestone(milestone)
|
||||
.create();
|
||||
assertNotNull(o);
|
||||
assertThat(o, notNullValue());
|
||||
o.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateAndListDeployments() throws IOException {
|
||||
GHRepository repository = getTestRepository();
|
||||
GHDeployment deployment = repository.createDeployment("master")
|
||||
GHDeployment deployment = repository.createDeployment("main")
|
||||
.payload("{\"user\":\"atmos\",\"room_id\":123456}")
|
||||
.description("question")
|
||||
.environment("unittest")
|
||||
.create();
|
||||
assertNotNull(deployment.getCreator());
|
||||
assertNotNull(deployment.getId());
|
||||
List<GHDeployment> deployments = repository.listDeployments(null, "master", null, "unittest").toList();
|
||||
assertNotNull(deployments);
|
||||
assertFalse(Iterables.isEmpty(deployments));
|
||||
GHDeployment unitTestDeployment = deployments.get(0);
|
||||
assertEquals("unittest", unitTestDeployment.getEnvironment());
|
||||
assertEquals("unittest", unitTestDeployment.getOriginalEnvironment());
|
||||
assertEquals(false, unitTestDeployment.isProductionEnvironment());
|
||||
assertEquals(true, unitTestDeployment.isTransientEnvironment());
|
||||
assertEquals("master", unitTestDeployment.getRef());
|
||||
try {
|
||||
assertThat(deployment.getCreator(), notNullValue());
|
||||
assertThat(deployment.getId(), notNullValue());
|
||||
List<GHDeployment> deployments = repository.listDeployments(null, "main", null, "unittest").toList();
|
||||
assertThat(deployments, notNullValue());
|
||||
assertThat(deployments, is(not(emptyIterable())));
|
||||
GHDeployment unitTestDeployment = deployments.get(0);
|
||||
assertThat(unitTestDeployment.getEnvironment(), equalTo("unittest"));
|
||||
assertThat(unitTestDeployment.getOriginalEnvironment(), equalTo("unittest"));
|
||||
assertThat(unitTestDeployment.isProductionEnvironment(), equalTo(false));
|
||||
assertThat(unitTestDeployment.isTransientEnvironment(), equalTo(false));
|
||||
assertThat(unitTestDeployment.getRef(), equalTo("main"));
|
||||
} finally {
|
||||
// deployment.delete();
|
||||
assert true;
|
||||
}
|
||||
}
|
||||
|
||||
@Ignore("Needs mocking check")
|
||||
@Test
|
||||
public void testGetDeploymentStatuses() throws IOException {
|
||||
GHRepository repository = getTestRepository();
|
||||
GHDeployment deployment = repository.createDeployment("master")
|
||||
GHDeployment deployment = repository.createDeployment("main")
|
||||
.description("question")
|
||||
.payload("{\"user\":\"atmos\",\"room_id\":123456}")
|
||||
.create();
|
||||
GHDeploymentStatus ghDeploymentStatus = deployment.createStatus(GHDeploymentState.QUEUED)
|
||||
.description("success")
|
||||
.targetUrl("http://www.github.com")
|
||||
.logUrl("http://www.github.com/logurl")
|
||||
.environmentUrl("http://www.github.com/envurl")
|
||||
.environment("new-ci-env")
|
||||
.create();
|
||||
Iterable<GHDeploymentStatus> deploymentStatuses = deployment.listStatuses();
|
||||
assertNotNull(deploymentStatuses);
|
||||
assertEquals(1, Iterables.size(deploymentStatuses));
|
||||
GHDeploymentStatus actualStatus = Iterables.get(deploymentStatuses, 0);
|
||||
assertEquals(ghDeploymentStatus.getId(), actualStatus.getId());
|
||||
assertEquals(ghDeploymentStatus.getState(), actualStatus.getState());
|
||||
assertEquals(ghDeploymentStatus.getLogUrl(), actualStatus.getLogUrl());
|
||||
// Target url was deprecated and replaced with log url. The gh api will
|
||||
// prefer the log url value and return it in place of target url.
|
||||
assertEquals(ghDeploymentStatus.getTargetUrl(), actualStatus.getLogUrl());
|
||||
try {
|
||||
GHDeploymentStatus ghDeploymentStatus = deployment.createStatus(GHDeploymentState.QUEUED)
|
||||
.description("success")
|
||||
.targetUrl("http://www.github.com")
|
||||
.logUrl("http://www.github.com/logurl")
|
||||
.environmentUrl("http://www.github.com/envurl")
|
||||
.environment("new-ci-env")
|
||||
.create();
|
||||
Iterable<GHDeploymentStatus> deploymentStatuses = deployment.listStatuses();
|
||||
assertThat(deploymentStatuses, notNullValue());
|
||||
assertThat(Iterables.size(deploymentStatuses), equalTo(1));
|
||||
GHDeploymentStatus actualStatus = Iterables.get(deploymentStatuses, 0);
|
||||
assertThat(actualStatus.getId(), equalTo(ghDeploymentStatus.getId()));
|
||||
assertThat(actualStatus.getState(), equalTo(ghDeploymentStatus.getState()));
|
||||
assertThat(actualStatus.getLogUrl(), equalTo(ghDeploymentStatus.getLogUrl()));
|
||||
// Target url was deprecated and replaced with log url. The gh api will
|
||||
// prefer the log url value and return it in place of target url.
|
||||
assertThat(actualStatus.getLogUrl(), equalTo(ghDeploymentStatus.getTargetUrl()));
|
||||
assertThat(ghDeploymentStatus.getDeploymentUrl(), equalTo(deployment.getUrl()));
|
||||
assertThat(ghDeploymentStatus.getRepositoryUrl(), equalTo(repository.getUrl()));
|
||||
} finally {
|
||||
// deployment.delete();
|
||||
assert true;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -216,7 +247,7 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
.getRepository("github-api")
|
||||
.getIssues(GHIssueState.CLOSED);
|
||||
// prior to using PagedIterable GHRepository.getIssues(GHIssueState) would only retrieve 30 issues
|
||||
assertTrue(closedIssues.size() > 150);
|
||||
assertThat(closedIssues.size(), greaterThan(150));
|
||||
String readRepoString = GitHub.getMappingObjectWriter().writeValueAsString(closedIssues.get(0));
|
||||
}
|
||||
|
||||
@@ -232,11 +263,11 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
|
||||
int x = 0;
|
||||
for (GHIssue issue : closedIssues) {
|
||||
assertNotNull(issue);
|
||||
assertThat(issue, notNullValue());
|
||||
x++;
|
||||
}
|
||||
|
||||
assertTrue(x > 150);
|
||||
assertThat(x, greaterThan(150));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -247,7 +278,7 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
@Test
|
||||
public void testMyOrganizations() throws IOException {
|
||||
Map<String, GHOrganization> org = gitHub.getMyOrganizations();
|
||||
assertFalse(org.keySet().contains(null));
|
||||
assertThat(org.containsKey(null), is(false));
|
||||
// System.out.println(org);
|
||||
}
|
||||
|
||||
@@ -257,7 +288,7 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
Map<String, GHOrganization> myOrganizations = gitHub.getMyOrganizations();
|
||||
// GitHub no longer has default 'owners' team, so there may be organization memberships without a team
|
||||
// https://help.github.com/articles/about-improved-organization-permissions/
|
||||
assertTrue(myOrganizations.keySet().containsAll(teams.keySet()));
|
||||
assertThat(myOrganizations.keySet().containsAll(teams.keySet()), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -267,7 +298,7 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
String organizationName = teamsPerOrg.getKey();
|
||||
for (GHTeam team : teamsPerOrg.getValue()) {
|
||||
String teamName = team.getName();
|
||||
assertTrue("Team " + teamName + " in organization " + organizationName + " does not contain myself",
|
||||
assertThat("Team " + teamName + " in organization " + organizationName + " does not contain myself",
|
||||
shouldBelongToTeam(organizationName, teamName));
|
||||
}
|
||||
}
|
||||
@@ -280,7 +311,7 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
user.login = "kohsuke";
|
||||
|
||||
Map<String, GHOrganization> orgs = gitHub.getUserPublicOrganizations(user);
|
||||
assertFalse(orgs.isEmpty());
|
||||
assertThat(orgs.size(), greaterThan(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -290,14 +321,14 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
user.login = "bitwiseman";
|
||||
|
||||
Map<String, GHOrganization> orgs = gitHub.getUserPublicOrganizations(user);
|
||||
assertTrue(orgs.isEmpty());
|
||||
assertThat(orgs.size(), equalTo(0));
|
||||
}
|
||||
|
||||
private boolean shouldBelongToTeam(String organizationName, String teamName) throws IOException {
|
||||
GHOrganization org = gitHub.getOrganization(organizationName);
|
||||
assertNotNull(org);
|
||||
assertThat(org, notNullValue());
|
||||
GHTeam team = org.getTeamByName(teamName);
|
||||
assertNotNull(team);
|
||||
assertThat(team, notNullValue());
|
||||
return team.hasMember(gitHub.getMyself());
|
||||
}
|
||||
|
||||
@@ -307,10 +338,10 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
GHTeam teamByName = organization.getTeams().get("Core Developers");
|
||||
|
||||
GHTeam teamById = gitHub.getTeam((int) teamByName.getId());
|
||||
assertNotNull(teamById);
|
||||
assertThat(teamById, notNullValue());
|
||||
|
||||
assertEquals(teamByName.getId(), teamById.getId());
|
||||
assertEquals(teamByName.getDescription(), teamById.getDescription());
|
||||
assertThat(teamById.getId(), equalTo(teamByName.getId()));
|
||||
assertThat(teamById.getDescription(), equalTo(teamByName.getDescription()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -319,16 +350,16 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
GHTeam teamByName = organization.getTeams().get("Core Developers");
|
||||
|
||||
GHTeam teamById = organization.getTeam(teamByName.getId());
|
||||
assertNotNull(teamById);
|
||||
assertThat(teamById, notNullValue());
|
||||
|
||||
assertEquals(teamByName.getId(), teamById.getId());
|
||||
assertEquals(teamByName.getDescription(), teamById.getDescription());
|
||||
assertThat(teamById.getId(), equalTo(teamByName.getId()));
|
||||
assertThat(teamById.getDescription(), equalTo(teamByName.getDescription()));
|
||||
|
||||
GHTeam teamById2 = organization.getTeam((int) teamByName.getId());
|
||||
assertNotNull(teamById2);
|
||||
GHTeam teamById2 = organization.getTeam(teamByName.getId());
|
||||
assertThat(teamById2, notNullValue());
|
||||
|
||||
assertEquals(teamByName.getId(), teamById2.getId());
|
||||
assertEquals(teamByName.getDescription(), teamById2.getDescription());
|
||||
assertThat(teamById2.getId(), equalTo(teamByName.getId()));
|
||||
assertThat(teamById2.getDescription(), equalTo(teamByName.getDescription()));
|
||||
|
||||
}
|
||||
|
||||
@@ -336,7 +367,8 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
@Test
|
||||
public void testFetchPullRequest() throws Exception {
|
||||
GHRepository r = gitHub.getOrganization("jenkinsci").getRepository("jenkins");
|
||||
assertEquals("master", r.getMasterBranch());
|
||||
assertThat(r.getMasterBranch(), equalTo("main"));
|
||||
assertThat(r.getDefaultBranch(), equalTo("main"));
|
||||
r.getPullRequest(1);
|
||||
r.getPullRequests(GHIssueState.OPEN);
|
||||
}
|
||||
@@ -345,11 +377,11 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
@Test
|
||||
public void testFetchPullRequestAsList() throws Exception {
|
||||
GHRepository r = gitHub.getRepository("hub4j/github-api");
|
||||
assertEquals("master", r.getMasterBranch());
|
||||
assertThat(r.getMasterBranch(), equalTo("main"));
|
||||
PagedIterable<GHPullRequest> i = r.listPullRequests(GHIssueState.CLOSED);
|
||||
List<GHPullRequest> prs = i.toList();
|
||||
assertNotNull(prs);
|
||||
assertTrue(prs.size() > 0);
|
||||
assertThat(prs, notNullValue());
|
||||
assertThat(prs, is(not(empty())));
|
||||
}
|
||||
|
||||
@Ignore("Needs mocking check")
|
||||
@@ -358,26 +390,26 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
kohsuke();
|
||||
|
||||
GHRepository r = gitHub.getOrganization(GITHUB_API_TEST_ORG).getRepository("github-api");
|
||||
assertTrue(r.hasPullAccess());
|
||||
assertThat(r.hasPullAccess(), is(true));
|
||||
|
||||
r = gitHub.getOrganization("github").getRepository("hub");
|
||||
assertFalse(r.hasAdminAccess());
|
||||
assertThat(r.hasAdminAccess(), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMyself() throws Exception {
|
||||
GHMyself me = gitHub.getMyself();
|
||||
assertNotNull(me);
|
||||
assertNotNull(gitHub.getUser("bitwiseman"));
|
||||
assertThat(me, notNullValue());
|
||||
assertThat(gitHub.getUser("bitwiseman"), notNullValue());
|
||||
PagedIterable<GHRepository> ghRepositories = me.listRepositories();
|
||||
assertTrue(ghRepositories.iterator().hasNext());
|
||||
assertThat(ghRepositories, is(not(emptyIterable())));
|
||||
}
|
||||
|
||||
@Ignore("Needs mocking check")
|
||||
@Test
|
||||
public void testPublicKeys() throws Exception {
|
||||
List<GHKey> keys = gitHub.getMyself().getPublicKeys();
|
||||
assertFalse(keys.isEmpty());
|
||||
assertThat(keys, is(not(empty())));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -390,8 +422,8 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
public void testGetTeamsForRepo() throws Exception {
|
||||
kohsuke();
|
||||
// 'Core Developers' and 'Owners'
|
||||
assertEquals(2,
|
||||
gitHub.getOrganization(GITHUB_API_TEST_ORG).getRepository("testGetTeamsForRepo").getTeams().size());
|
||||
assertThat(gitHub.getOrganization(GITHUB_API_TEST_ORG).getRepository("testGetTeamsForRepo").getTeams().size(),
|
||||
equalTo(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -413,24 +445,24 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
kohsuke();
|
||||
int sz = 0;
|
||||
for (GHTeam t : gitHub.getOrganization(GITHUB_API_TEST_ORG).listTeams()) {
|
||||
assertNotNull(t.getName());
|
||||
assertThat(t.getName(), notNullValue());
|
||||
sz++;
|
||||
}
|
||||
assertTrue(sz < 100);
|
||||
assertThat(sz, lessThan(100));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOrgTeamByName() throws Exception {
|
||||
kohsuke();
|
||||
GHTeam e = gitHub.getOrganization(GITHUB_API_TEST_ORG).getTeamByName("Core Developers");
|
||||
assertNotNull(e);
|
||||
assertThat(e, notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOrgTeamBySlug() throws Exception {
|
||||
kohsuke();
|
||||
GHTeam e = gitHub.getOrganization(GITHUB_API_TEST_ORG).getTeamBySlug("core-developers");
|
||||
assertNotNull(e);
|
||||
assertThat(e, notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -438,20 +470,30 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
GHCommit commit = gitHub.getUser("jenkinsci")
|
||||
.getRepository("jenkins")
|
||||
.getCommit("08c1c9970af4d609ae754fbe803e06186e3206f7");
|
||||
assertEquals(1, commit.getParents().size());
|
||||
assertEquals(1, commit.getFiles().size());
|
||||
assertEquals("https://github.com/jenkinsci/jenkins/commit/08c1c9970af4d609ae754fbe803e06186e3206f7",
|
||||
commit.getHtmlUrl().toString());
|
||||
assertThat(commit.getParents().size(), equalTo(1));
|
||||
assertThat(commit.getFiles().size(), equalTo(1));
|
||||
assertThat(commit.getHtmlUrl().toString(),
|
||||
equalTo("https://github.com/jenkinsci/jenkins/commit/08c1c9970af4d609ae754fbe803e06186e3206f7"));
|
||||
|
||||
File f = commit.getFiles().get(0);
|
||||
assertEquals(48, f.getLinesChanged());
|
||||
assertEquals("modified", f.getStatus());
|
||||
assertEquals("changelog.html", f.getFileName());
|
||||
assertThat(f.getLinesChanged(), equalTo(48));
|
||||
assertThat(f.getLinesAdded(), equalTo(40));
|
||||
assertThat(f.getLinesDeleted(), equalTo(8));
|
||||
assertThat(f.getPreviousFilename(), nullValue());
|
||||
assertThat(f.getPatch(), startsWith("@@ -54,6 +54,14 @@\n"));
|
||||
assertThat(f.getSha(), equalTo("04d3e54017542ad0ff46355eababacd4850ccba5"));
|
||||
assertThat(f.getBlobUrl().toString(),
|
||||
equalTo("https://github.com/jenkinsci/jenkins/blob/08c1c9970af4d609ae754fbe803e06186e3206f7/changelog.html"));
|
||||
assertThat(f.getRawUrl().toString(),
|
||||
equalTo("https://github.com/jenkinsci/jenkins/raw/08c1c9970af4d609ae754fbe803e06186e3206f7/changelog.html"));
|
||||
|
||||
assertThat(f.getStatus(), equalTo("modified"));
|
||||
assertThat(f.getFileName(), equalTo("changelog.html"));
|
||||
|
||||
// walk the tree
|
||||
GHTree t = commit.getTree();
|
||||
assertThat(IOUtils.toString(t.getEntry("todo.txt").readAsBlob()), containsString("executor rendering"));
|
||||
assertNotNull(t.getEntry("war").asTree());
|
||||
assertThat(t.getEntry("war").asTree(), notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -460,24 +502,8 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
for (GHCommit c : gitHub.getUser("kohsuke").getRepository("empty-commit").listCommits()) {
|
||||
sha1.add(c.getSHA1());
|
||||
}
|
||||
assertEquals("fdfad6be4db6f96faea1f153fb447b479a7a9cb7", sha1.get(0));
|
||||
assertEquals(1, sha1.size());
|
||||
}
|
||||
|
||||
public void testQueryCommits() throws Exception {
|
||||
List<String> sha1 = new ArrayList<String>();
|
||||
for (GHCommit c : gitHub.getUser("jenkinsci")
|
||||
.getRepository("jenkins")
|
||||
.queryCommits()
|
||||
.since(new Date(1199174400000L))
|
||||
.until(1201852800000L)
|
||||
.path("pom.xml")
|
||||
.list()) {
|
||||
// System.out.println(c.getSHA1());
|
||||
sha1.add(c.getSHA1());
|
||||
}
|
||||
assertEquals("1cccddb22e305397151b2b7b87b4b47d74ca337b", sha1.get(0));
|
||||
assertEquals(29, sha1.size());
|
||||
assertThat(sha1.get(0), equalTo("fdfad6be4db6f96faea1f153fb447b479a7a9cb7"));
|
||||
assertThat(sha1.size(), equalTo(1));
|
||||
}
|
||||
|
||||
@Ignore("Needs mocking check")
|
||||
@@ -494,7 +520,7 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
List<GHCommitComment> batch = comments.iterator().nextPage();
|
||||
for (GHCommitComment comment : batch) {
|
||||
// System.out.println(comment.getBody());
|
||||
assertSame(comment.getOwner(), r);
|
||||
assertThat(r, sameInstance(comment.getOwner()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -504,23 +530,65 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
.getRepository("sandbox-ant")
|
||||
.getCommit("8ae38db0ea5837313ab5f39d43a6f73de3bd9000");
|
||||
GHCommitComment c = commit.createComment("[testing](http://kohsuse.org/)");
|
||||
// System.out.println(c);
|
||||
c.update("updated text");
|
||||
// System.out.println(c);
|
||||
c.delete();
|
||||
try {
|
||||
assertThat(c.getPath(), nullValue());
|
||||
assertThat(c.getLine(), equalTo(-1));
|
||||
assertThat(c.getHtmlUrl().toString(),
|
||||
containsString(
|
||||
"kohsuke/sandbox-ant/commit/8ae38db0ea5837313ab5f39d43a6f73de3bd9000#commitcomment-"));
|
||||
assertThat(c.listReactions().toList(), is(empty()));
|
||||
|
||||
c.update("updated text");
|
||||
assertThat(c.getBody(), equalTo("updated text"));
|
||||
} finally {
|
||||
c.delete();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tryHook() throws Exception {
|
||||
kohsuke();
|
||||
GHRepository r = gitHub.getOrganization(GITHUB_API_TEST_ORG).getRepository("github-api");
|
||||
GHHook hook = r.createWebHook(new URL("http://www.google.com/"));
|
||||
// System.out.println(hook);
|
||||
GHOrganization o = gitHub.getOrganization(GITHUB_API_TEST_ORG);
|
||||
GHRepository r = o.getRepository("github-api");
|
||||
try {
|
||||
GHHook hook = r.createWebHook(new URL("http://www.google.com/"));
|
||||
assertThat(hook.getName(), equalTo("web"));
|
||||
assertThat(hook.getEvents().size(), equalTo(1));
|
||||
assertThat(hook.getEvents(), contains(GHEvent.PUSH));
|
||||
assertThat(hook.getConfig().size(), equalTo(3));
|
||||
assertThat(hook.isActive(), equalTo(true));
|
||||
|
||||
if (mockGitHub.isUseProxy()) {
|
||||
r = getGitHubBeforeAfter().getOrganization(GITHUB_API_TEST_ORG).getRepository("github-api");
|
||||
for (GHHook h : r.getHooks()) {
|
||||
h.delete();
|
||||
GHHook hook2 = r.getHook((int) hook.getId());
|
||||
assertThat(hook2.getName(), equalTo("web"));
|
||||
assertThat(hook2.getEvents().size(), equalTo(1));
|
||||
assertThat(hook2.getEvents(), contains(GHEvent.PUSH));
|
||||
assertThat(hook2.getConfig().size(), equalTo(3));
|
||||
assertThat(hook2.isActive(), equalTo(true));
|
||||
hook2.ping();
|
||||
hook2.delete();
|
||||
|
||||
hook = o.createWebHook(new URL("http://www.google.com/"));
|
||||
assertThat(hook.getName(), equalTo("web"));
|
||||
assertThat(hook.getEvents().size(), equalTo(1));
|
||||
assertThat(hook.getEvents(), contains(GHEvent.PUSH));
|
||||
assertThat(hook.getConfig().size(), equalTo(3));
|
||||
assertThat(hook.isActive(), equalTo(true));
|
||||
|
||||
hook2 = o.getHook((int) hook.getId());
|
||||
assertThat(hook2.getName(), equalTo("web"));
|
||||
assertThat(hook2.getEvents().size(), equalTo(1));
|
||||
assertThat(hook2.getEvents(), contains(GHEvent.PUSH));
|
||||
assertThat(hook2.getConfig().size(), equalTo(3));
|
||||
assertThat(hook2.isActive(), equalTo(true));
|
||||
hook2.ping();
|
||||
hook2.delete();
|
||||
|
||||
// System.out.println(hook);
|
||||
} finally {
|
||||
if (mockGitHub.isUseProxy()) {
|
||||
r = getNonRecordingGitHub().getOrganization(GITHUB_API_TEST_ORG).getRepository("github-api");
|
||||
for (GHHook h : r.getHooks()) {
|
||||
h.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -529,6 +597,14 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
public void testEventApi() throws Exception {
|
||||
for (GHEventInfo ev : gitHub.getEvents()) {
|
||||
if (ev.getType() == GHEvent.PULL_REQUEST) {
|
||||
if (ev.getId() == 10680625394L) {
|
||||
assertThat(ev.getActorLogin(), equalTo("pull[bot]"));
|
||||
assertThat(ev.getOrganization(), nullValue());
|
||||
assertThat(ev.getRepository().getFullName(), equalTo("daddyfatstacksBIG/lerna"));
|
||||
assertThat(ev.getCreatedAt(), equalTo(GitHubClient.parseDate("2019-10-21T21:54:52Z")));
|
||||
assertThat(ev.getType(), equalTo(GHEvent.PULL_REQUEST));
|
||||
}
|
||||
|
||||
GHEventPayload.PullRequest pr = ev.getPayload(GHEventPayload.PullRequest.class);
|
||||
assertThat(pr.getNumber(), is(pr.getPullRequest().getNumber()));
|
||||
}
|
||||
@@ -643,7 +719,7 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
GHOrganization j = gitHub.getOrganization(GITHUB_API_TEST_ORG);
|
||||
GHTeam t = j.getTeams().get("Core Developers");
|
||||
|
||||
assertNotNull(j.getRepository("jenkins"));
|
||||
assertThat(j.getRepository("jenkins"), notNullValue());
|
||||
|
||||
// t.add(labs.getRepository("xyz"));
|
||||
}
|
||||
@@ -660,18 +736,18 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
List<GHCommitStatus> lst = r.listCommitStatuses("ecbfdd7315ef2cf04b2be7f11a072ce0bd00c396").toList();
|
||||
state = lst.get(0);
|
||||
// System.out.println(state);
|
||||
assertEquals("testing!", state.getDescription());
|
||||
assertEquals("http://kohsuke.org/", state.getTargetUrl());
|
||||
assertThat(state.getDescription(), equalTo("testing!"));
|
||||
assertThat(state.getTargetUrl(), equalTo("http://kohsuke.org/"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCommitShortInfo() throws Exception {
|
||||
GHRepository r = gitHub.getRepository("hub4j/github-api");
|
||||
GHCommit commit = r.getCommit("86a2e245aa6d71d54923655066049d9e21a15f23");
|
||||
assertEquals(commit.getCommitShortInfo().getAuthor().getName(), "Kohsuke Kawaguchi");
|
||||
assertEquals(commit.getCommitShortInfo().getMessage(), "doc");
|
||||
assertFalse(commit.getCommitShortInfo().getVerification().isVerified());
|
||||
assertEquals(commit.getCommitShortInfo().getVerification().getReason(), GHVerification.Reason.UNSIGNED);
|
||||
assertThat("Kohsuke Kawaguchi", equalTo(commit.getCommitShortInfo().getAuthor().getName()));
|
||||
assertThat("doc", equalTo(commit.getCommitShortInfo().getMessage()));
|
||||
assertThat(commit.getCommitShortInfo().getVerification().isVerified(), is(false));
|
||||
assertThat(GHVerification.Reason.UNSIGNED, equalTo(commit.getCommitShortInfo().getVerification().getReason()));
|
||||
assertThat(commit.getCommitShortInfo().getAuthor().getDate().toInstant().getEpochSecond(),
|
||||
equalTo(1271650361L));
|
||||
assertThat(commit.getCommitShortInfo().getCommitter().getDate().toInstant().getEpochSecond(),
|
||||
@@ -684,7 +760,7 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
GHRepository r = gitHub.getUser("kohsuke").getRepository("github-api");
|
||||
GHPullRequest p = r.getPullRequest(17);
|
||||
GHUser u = p.getUser();
|
||||
assertNotNull(u.getName());
|
||||
assertThat(u.getName(), notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -694,11 +770,11 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
GHUser kohsuke = gitHub.getUser("kohsuke");
|
||||
GHUser b = gitHub.getUser("b");
|
||||
|
||||
assertTrue(j.hasMember(kohsuke));
|
||||
assertFalse(j.hasMember(b));
|
||||
assertThat(j.hasMember(kohsuke), is(true));
|
||||
assertThat(j.hasMember(b), is(false));
|
||||
|
||||
assertTrue(j.hasPublicMember(kohsuke));
|
||||
assertFalse(j.hasPublicMember(b));
|
||||
assertThat(j.hasPublicMember(kohsuke), is(true));
|
||||
assertThat(j.hasPublicMember(b), is(false));
|
||||
}
|
||||
|
||||
@Ignore("Needs mocking check")
|
||||
@@ -721,7 +797,7 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
if (tagName.equals(tag.getName())) {
|
||||
String ash = tag.getCommit().getSHA1();
|
||||
GHRef ref = r.createRef("refs/heads/" + releaseName, ash);
|
||||
assertEquals(ref.getRef(), "refs/heads/" + releaseName);
|
||||
assertThat(("refs/heads/" + releaseName), equalTo(ref.getRef()));
|
||||
|
||||
for (Map.Entry<String, GHBranch> entry : r.getBranches().entrySet()) {
|
||||
// System.out.println(entry.getKey() + "/" + entry.getValue());
|
||||
@@ -740,9 +816,9 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
|
||||
@Test
|
||||
public void testRef() throws IOException {
|
||||
GHRef masterRef = gitHub.getRepository("jenkinsci/jenkins").getRef("heads/master");
|
||||
assertEquals(mockGitHub.apiServer().baseUrl() + "/repos/jenkinsci/jenkins/git/refs/heads/master",
|
||||
masterRef.getUrl().toString());
|
||||
GHRef mainRef = gitHub.getRepository("jenkinsci/jenkins").getRef("heads/main");
|
||||
assertThat(mainRef.getUrl().toString(),
|
||||
equalTo(mockGitHub.apiServer().baseUrl() + "/repos/jenkinsci/jenkins/git/refs/heads/main"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -765,14 +841,14 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
final GHDeployKey newDeployKey = myRepository.addDeployKey("test",
|
||||
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDUt0RAycC5cS42JKh6SecfFZBR1RrF+2hYMctz4mk74/arBE+wFb7fnSHGzdGKX2h5CFOWODifRCJVhB7hlVxodxe+QkQQYAEL/x1WVCJnGgTGQGOrhOMj95V3UE5pQKhsKD608C+u5tSofcWXLToP1/wZ7U4/AHjqYi08OLsWToHCax55TZkvdt2jo0hbIoYU+XI9Q8Uv4ONDN1oabiOdgeKi8+crvHAuvNleiBhWVBzFh8KdfzaH5uNdw7ihhFjEd1vzqACsjCINCjdMfzl6jD9ExuWuE92nZJnucls2cEoNC6k2aPmrZDg9hA32FXVpyseY+bDUWFU6LO2LG6PB kohsuke@atlas");
|
||||
try {
|
||||
assertNotNull(newDeployKey.getId());
|
||||
assertThat(newDeployKey.getId(), notNullValue());
|
||||
|
||||
GHDeployKey k = Iterables.find(myRepository.getDeployKeys(), new Predicate<GHDeployKey>() {
|
||||
public boolean apply(GHDeployKey deployKey) {
|
||||
return newDeployKey.getId() == deployKey.getId();
|
||||
}
|
||||
});
|
||||
assertNotNull(k);
|
||||
assertThat(k, notNullValue());
|
||||
} finally {
|
||||
newDeployKey.delete();
|
||||
}
|
||||
@@ -782,10 +858,10 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
@Test
|
||||
public void testCommitStatusContext() throws IOException {
|
||||
GHRepository myRepository = getTestRepository();
|
||||
GHRef masterRef = myRepository.getRef("heads/master");
|
||||
GHCommitStatus commitStatus = myRepository.createCommitStatus(masterRef.getObject()
|
||||
GHRef mainRef = myRepository.getRef("heads/main");
|
||||
GHCommitStatus commitStatus = myRepository.createCommitStatus(mainRef.getObject()
|
||||
.getSha(), GHCommitState.SUCCESS, "http://www.example.com", "test", "test/context");
|
||||
assertEquals("test/context", commitStatus.getContext());
|
||||
assertThat(commitStatus.getContext(), equalTo("test/context"));
|
||||
|
||||
}
|
||||
|
||||
@@ -797,7 +873,7 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
// System.out.println(u.getLogin());
|
||||
all.add(u);
|
||||
}
|
||||
assertFalse(all.isEmpty());
|
||||
assertThat(all, is(not(empty())));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -808,10 +884,10 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
.author("kohsuke")
|
||||
.sort(GHCommitSearchBuilder.Sort.COMMITTER_DATE)
|
||||
.list();
|
||||
assertTrue(r.getTotalCount() > 0);
|
||||
assertThat(r.getTotalCount(), greaterThan(0));
|
||||
|
||||
GHCommit firstCommit = r.iterator().next();
|
||||
assertTrue(firstCommit.getFiles().size() > 0);
|
||||
assertThat(firstCommit.getFiles(), is(not(empty())));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -821,7 +897,7 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
.isOpen()
|
||||
.sort(GHIssueSearchBuilder.Sort.UPDATED)
|
||||
.list();
|
||||
assertTrue(r.getTotalCount() > 0);
|
||||
assertThat(r.getTotalCount(), greaterThan(0));
|
||||
for (GHIssue issue : r) {
|
||||
assertThat(issue.getTitle(), notNullValue());
|
||||
PagedIterable<GHIssueComment> comments = issue.listComments();
|
||||
@@ -834,35 +910,45 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
@Test // issue #99
|
||||
public void testReadme() throws IOException {
|
||||
GHContent readme = gitHub.getRepository("hub4j-test-org/test-readme").getReadme();
|
||||
assertEquals(readme.getName(), "README.md");
|
||||
assertEquals(readme.getContent(), "This is a markdown readme.\n");
|
||||
assertThat("README.md", equalTo(readme.getName()));
|
||||
assertThat("This is a markdown readme.\n", equalTo(readme.getContent()));
|
||||
}
|
||||
|
||||
@Ignore("Needs mocking check")
|
||||
@Test
|
||||
public void testTrees() throws IOException {
|
||||
GHTree masterTree = gitHub.getRepository("hub4j/github-api").getTree("master");
|
||||
GHTree mainTree = gitHub.getRepository("hub4j/github-api").getTree("main");
|
||||
boolean foundReadme = false;
|
||||
for (GHTreeEntry e : masterTree.getTree()) {
|
||||
for (GHTreeEntry e : mainTree.getTree()) {
|
||||
if ("readme".equalsIgnoreCase(e.getPath().replaceAll("\\.md", ""))) {
|
||||
foundReadme = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assertTrue(foundReadme);
|
||||
assertThat(foundReadme, is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTreesRecursive() throws IOException {
|
||||
GHTree masterTree = gitHub.getRepository("hub4j/github-api").getTreeRecursive("master", 1);
|
||||
GHTree mainTree = gitHub.getRepository("hub4j/github-api").getTreeRecursive("main", 1);
|
||||
boolean foundThisFile = false;
|
||||
for (GHTreeEntry e : masterTree.getTree()) {
|
||||
for (GHTreeEntry e : mainTree.getTree()) {
|
||||
if (e.getPath().endsWith(AppTest.class.getSimpleName() + ".java")) {
|
||||
foundThisFile = true;
|
||||
assertThat(e.getPath(), equalTo("src/test/java/org/kohsuke/github/AppTest.java"));
|
||||
assertThat(e.getSha(), equalTo("baad7a7c4cf409f610a0e8c7eba17664eb655c44"));
|
||||
assertThat(e.getMode(), equalTo("100755"));
|
||||
assertThat(e.getSize(), greaterThan(30000L));
|
||||
assertThat(e.getUrl().toString(),
|
||||
containsString("/repos/hub4j/github-api/git/blobs/baad7a7c4cf409f610a0e8c7eba17664eb655c44"));
|
||||
GHBlob blob = e.asBlob();
|
||||
assertThat(e.asBlob().getUrl().toString(),
|
||||
containsString("/repos/hub4j/github-api/git/blobs/baad7a7c4cf409f610a0e8c7eba17664eb655c44"));
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
assertTrue(foundThisFile);
|
||||
assertThat(foundThisFile, is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -875,11 +961,14 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
for (GHLabel l : lst) {
|
||||
assertThat(l.getUrl(), containsString(l.getName().replace(" ", "%20")));
|
||||
}
|
||||
assertTrue(lst.size() > 5);
|
||||
assertThat(lst.size(), greaterThan(5));
|
||||
GHLabel e = r.getLabel("enhancement");
|
||||
assertEquals("enhancement", e.getName());
|
||||
assertNotNull(e.getUrl());
|
||||
assertTrue(Pattern.matches("[0-9a-fA-F]{6}", e.getColor()));
|
||||
assertThat(e.getName(), equalTo("enhancement"));
|
||||
assertThat(e.getUrl(), notNullValue());
|
||||
assertThat(e.getId(), equalTo(177339106L));
|
||||
assertThat(e.getNodeId(), equalTo("MDU6TGFiZWwxNzczMzkxMDY="));
|
||||
assertThat(e.isDefault(), is(true));
|
||||
assertThat(e.getColor(), matchesPattern("[0-9a-fA-F]{6}"));
|
||||
|
||||
GHLabel t = null;
|
||||
GHLabel t2 = null;
|
||||
@@ -890,12 +979,17 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
assertThat(t, not(sameInstance(t2)));
|
||||
assertThat(t, equalTo(t2));
|
||||
|
||||
assertEquals(t.getName(), t2.getName());
|
||||
assertEquals(t.getColor(), "123456");
|
||||
assertEquals(t.getColor(), t2.getColor());
|
||||
assertEquals(t.getDescription(), "");
|
||||
assertEquals(t.getDescription(), t2.getDescription());
|
||||
assertEquals(t.getUrl(), t2.getUrl());
|
||||
assertThat(t2.isDefault(), is(false));
|
||||
|
||||
assertThat(t2.getId(), equalTo(t.getId()));
|
||||
assertThat(t2.getNodeId(), equalTo(t.getNodeId()));
|
||||
assertThat(t2.getName(), equalTo(t.getName()));
|
||||
assertThat("123456", equalTo(t.getColor()));
|
||||
assertThat(t2.getColor(), equalTo(t.getColor()));
|
||||
assertThat("", equalTo(t.getDescription()));
|
||||
assertThat(t2.getDescription(), equalTo(t.getDescription()));
|
||||
assertThat(t2.getUrl(), equalTo(t.getUrl()));
|
||||
assertThat(t2.isDefault(), equalTo(t.isDefault()));
|
||||
|
||||
// update works on multiple changes in one call
|
||||
t3 = t.update().color("000000").description("It is dark!").done();
|
||||
@@ -907,10 +1001,10 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
assertThat(t, not(sameInstance(t3)));
|
||||
assertThat(t, not(equalTo(t3)));
|
||||
|
||||
assertEquals(t.getColor(), "123456");
|
||||
assertEquals(t.getDescription(), "");
|
||||
assertEquals(t3.getColor(), "000000");
|
||||
assertEquals(t3.getDescription(), "It is dark!");
|
||||
assertThat("123456", equalTo(t.getColor()));
|
||||
assertThat("", equalTo(t.getDescription()));
|
||||
assertThat("000000", equalTo(t3.getColor()));
|
||||
assertThat("It is dark!", equalTo(t3.getDescription()));
|
||||
|
||||
// Test deprecated methods
|
||||
t.setDescription("Deprecated");
|
||||
@@ -918,13 +1012,13 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
|
||||
// By using the old instance t when calling setDescription it also sets color to the old value
|
||||
// this is a bad behavior, but it is expected
|
||||
assertEquals(t.getColor(), "123456");
|
||||
assertEquals(t.getDescription(), "Deprecated");
|
||||
assertThat("123456", equalTo(t.getColor()));
|
||||
assertThat("Deprecated", equalTo(t.getDescription()));
|
||||
|
||||
t.setColor("000000");
|
||||
t = r.getLabel("test");
|
||||
assertEquals(t.getColor(), "000000");
|
||||
assertEquals(t.getDescription(), "Deprecated");
|
||||
assertThat("000000", equalTo(t.getColor()));
|
||||
assertThat("Deprecated", equalTo(t.getDescription()));
|
||||
|
||||
// set() makes a single change
|
||||
t3 = t.set().description("this is also a test");
|
||||
@@ -933,8 +1027,8 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
assertThat(t, not(sameInstance(t3)));
|
||||
assertThat(t, not(equalTo(t3)));
|
||||
|
||||
assertEquals(t3.getColor(), "000000");
|
||||
assertEquals(t3.getDescription(), "this is also a test");
|
||||
assertThat("000000", equalTo(t3.getColor()));
|
||||
assertThat("this is also a test", equalTo(t3.getDescription()));
|
||||
|
||||
t.delete();
|
||||
try {
|
||||
@@ -947,12 +1041,12 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
t = r.createLabel("test2", "123457", "this is a different test");
|
||||
t2 = r.getLabel("test2");
|
||||
|
||||
assertEquals(t.getName(), t2.getName());
|
||||
assertEquals(t.getColor(), "123457");
|
||||
assertEquals(t.getColor(), t2.getColor());
|
||||
assertEquals(t.getDescription(), "this is a different test");
|
||||
assertEquals(t.getDescription(), t2.getDescription());
|
||||
assertEquals(t.getUrl(), t2.getUrl());
|
||||
assertThat(t2.getName(), equalTo(t.getName()));
|
||||
assertThat("123457", equalTo(t.getColor()));
|
||||
assertThat(t2.getColor(), equalTo(t.getColor()));
|
||||
assertThat("this is a different test", equalTo(t.getDescription()));
|
||||
assertThat(t2.getDescription(), equalTo(t.getDescription()));
|
||||
assertThat(t2.getUrl(), equalTo(t.getUrl()));
|
||||
t.delete();
|
||||
|
||||
// Allow null description
|
||||
@@ -969,7 +1063,7 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
void cleanupLabel(String name) {
|
||||
if (mockGitHub.isUseProxy()) {
|
||||
try {
|
||||
GHLabel t = getGitHubBeforeAfter().getRepository("hub4j-test-org/test-labels").getLabel(name);
|
||||
GHLabel t = getNonRecordingGitHub().getRepository("hub4j-test-org/test-labels").getLabel(name);
|
||||
t.delete();
|
||||
} catch (IOException e) {
|
||||
|
||||
@@ -984,13 +1078,13 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
for (GHUser u : mr.listSubscribers()) {
|
||||
bitwiseman |= u.getLogin().equals("bitwiseman");
|
||||
}
|
||||
assertTrue(bitwiseman);
|
||||
assertThat(bitwiseman, is(true));
|
||||
|
||||
boolean githubApiFound = false;
|
||||
for (GHRepository r : gitHub.getUser("bitwiseman").listRepositories()) {
|
||||
githubApiFound |= r.equals(mr);
|
||||
}
|
||||
assertTrue(githubApiFound);
|
||||
assertThat(githubApiFound, is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1021,7 +1115,7 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
assertThat(t.getCreatedAt(), nullValue());
|
||||
|
||||
}
|
||||
assertTrue(found);
|
||||
assertThat(found, is(true));
|
||||
gitHub.listNotifications().markAsRead();
|
||||
}
|
||||
|
||||
@@ -1101,8 +1195,8 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
GHMyself me = gitHub.getMyself();
|
||||
for (GHMembership m : me.listOrgMemberships()) {
|
||||
assertThat(m.getUser(), is((GHUser) me));
|
||||
assertNotNull(m.getState());
|
||||
assertNotNull(m.getRole());
|
||||
assertThat(m.getState(), notNullValue());
|
||||
assertThat(m.getRole(), notNullValue());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1113,15 +1207,15 @@ public class AppTest extends AbstractGitHubWireMockTest {
|
||||
GHRepository r = gitHub.getRepository("hub4j/github-api");
|
||||
String sha1 = "a12243f2fc5b8c2ba47dd677d0b0c7583539584d";
|
||||
|
||||
assertBlobContent(r.readBlob(sha1));
|
||||
verifyBlobContent(r.readBlob(sha1));
|
||||
|
||||
GHBlob blob = r.getBlob(sha1);
|
||||
assertBlobContent(blob.read());
|
||||
verifyBlobContent(blob.read());
|
||||
assertThat(blob.getSha(), is("a12243f2fc5b8c2ba47dd677d0b0c7583539584d"));
|
||||
assertThat(blob.getSize(), is(1104L));
|
||||
}
|
||||
|
||||
private void assertBlobContent(InputStream is) throws Exception {
|
||||
private void verifyBlobContent(InputStream is) throws Exception {
|
||||
String content = new String(IOUtils.toByteArray(is), StandardCharsets.UTF_8);
|
||||
assertThat(content, containsString("Copyright (c) 2011- Kohsuke Kawaguchi and other contributors"));
|
||||
assertThat(content, containsString("FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR"));
|
||||
|
||||
@@ -1,20 +1,42 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import com.tngtech.archunit.base.DescribedPredicate;
|
||||
import com.tngtech.archunit.core.domain.JavaAnnotation;
|
||||
import com.tngtech.archunit.core.domain.JavaClasses;
|
||||
import com.tngtech.archunit.core.domain.*;
|
||||
import com.tngtech.archunit.core.domain.properties.HasName;
|
||||
import com.tngtech.archunit.core.domain.properties.HasOwner;
|
||||
import com.tngtech.archunit.core.importer.ClassFileImporter;
|
||||
import com.tngtech.archunit.core.importer.ImportOption;
|
||||
import com.tngtech.archunit.lang.ArchCondition;
|
||||
import com.tngtech.archunit.lang.ArchRule;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import static com.tngtech.archunit.lang.conditions.ArchConditions.beAnnotatedWith;
|
||||
import static com.tngtech.archunit.lang.conditions.ArchConditions.not;
|
||||
import java.io.Closeable;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.tngtech.archunit.core.domain.JavaCall.Predicates.target;
|
||||
import static com.tngtech.archunit.core.domain.JavaClass.Predicates.resideInAPackage;
|
||||
import static com.tngtech.archunit.core.domain.JavaClass.Predicates.type;
|
||||
import static com.tngtech.archunit.core.domain.JavaClass.namesOf;
|
||||
import static com.tngtech.archunit.core.domain.properties.HasName.Predicates.name;
|
||||
import static com.tngtech.archunit.core.domain.properties.HasName.Predicates.nameContaining;
|
||||
import static com.tngtech.archunit.core.domain.properties.HasOwner.Predicates.With.owner;
|
||||
import static com.tngtech.archunit.core.domain.properties.HasParameterTypes.Predicates.rawParameterTypes;
|
||||
import static com.tngtech.archunit.lang.conditions.ArchConditions.*;
|
||||
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
|
||||
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.fields;
|
||||
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.methods;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
|
||||
public class ArchTests {
|
||||
|
||||
@@ -23,6 +45,13 @@ public class ArchTests {
|
||||
.withImportOption(new ImportOption.DoNotIncludeJars())
|
||||
.importPackages("org.kohsuke.github");
|
||||
|
||||
private static final JavaClasses apacheCommons = new ClassFileImporter().importPackages("org.apache.commons.lang3");
|
||||
|
||||
private static final JavaClasses testClassFiles = new ClassFileImporter()
|
||||
.withImportOption(new ImportOption.OnlyIncludeTests())
|
||||
.withImportOption(new ImportOption.DoNotIncludeJars())
|
||||
.importPackages("org.kohsuke.github");
|
||||
|
||||
private static final DescribedPredicate<JavaAnnotation<?>> previewAnnotationWithNoMediaType = new DescribedPredicate<JavaAnnotation<?>>(
|
||||
"preview has no required media types defined") {
|
||||
|
||||
@@ -36,7 +65,7 @@ public class ArchTests {
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() {
|
||||
assertTrue(classFiles.size() > 0);
|
||||
assertThat(classFiles.size(), greaterThan(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -105,4 +134,107 @@ public class ArchTests {
|
||||
methodRule.check(classFiles);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequireUseOfAssertThat() {
|
||||
|
||||
final String reason = "This project uses `assertThat(...)` instead of other `assert*()` methods.";
|
||||
|
||||
final DescribedPredicate<HasName> assertMethodOtherThanAssertThat = nameContaining("assert")
|
||||
.and(DescribedPredicate.not(name("assertThat")));
|
||||
|
||||
final ArchRule onlyAssertThatRule = classes()
|
||||
.should(not(callMethodWhere(target(assertMethodOtherThanAssertThat))))
|
||||
.because(reason);
|
||||
|
||||
onlyAssertThatRule.check(testClassFiles);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequireUseOfOnlySpecificApacheCommons() {
|
||||
|
||||
final ArchRule onlyApprovedApacheCommonsMethods = classes()
|
||||
.should(notCallMethodsInPackageUnless("org.apache.commons..",
|
||||
// unless it is one of these methods
|
||||
targetMethodIs(StringUtils.class, "capitalize", String.class),
|
||||
targetMethodIs(StringUtils.class, "defaultString", String.class, String.class),
|
||||
targetMethodIs(StringUtils.class, "equals", CharSequence.class, CharSequence.class),
|
||||
targetMethodIs(StringUtils.class, "isBlank", CharSequence.class),
|
||||
targetMethodIs(StringUtils.class, "isEmpty", CharSequence.class),
|
||||
targetMethodIs(StringUtils.class, "join", Iterable.class, String.class),
|
||||
targetMethodIs(StringUtils.class,
|
||||
"prependIfMissing",
|
||||
String.class,
|
||||
CharSequence.class,
|
||||
CharSequence[].class),
|
||||
targetMethodIs(ToStringBuilder.class, "toString"),
|
||||
targetMethodIs(ToStringBuilder.class, "append", String.class, Object.class),
|
||||
targetMethodIs(ToStringBuilder.class, "append", String.class, long.class),
|
||||
targetMethodIs(ToStringBuilder.class, "append", String.class, int.class),
|
||||
targetMethodIs(ToStringBuilder.class, "isEmpty"),
|
||||
targetMethodIs(ToStringBuilder.class, "equals"),
|
||||
targetMethodIs(ToStringBuilder.class, "capitalize"),
|
||||
targetMethodIs(ToStringStyle.class,
|
||||
"append",
|
||||
StringBuffer.class,
|
||||
String.class,
|
||||
Object.class,
|
||||
Boolean.class),
|
||||
targetMethodIs(ReflectionToStringBuilder.class, "accept", Field.class),
|
||||
targetMethodIs(IOUtils.class, "closeQuietly", InputStream.class),
|
||||
targetMethodIs(IOUtils.class, "closeQuietly", Closeable.class),
|
||||
targetMethodIs(IOUtils.class, "toString", InputStream.class, Charset.class),
|
||||
targetMethodIs(IOUtils.class, "toString", Reader.class),
|
||||
targetMethodIs(IOUtils.class, "toByteArray", InputStream.class)))
|
||||
.because(
|
||||
"Commons methods must be manually verified to be compatible with commons-io:2.4 or earlier and commons-lang3:3.9 or earlier.");
|
||||
|
||||
onlyApprovedApacheCommonsMethods.check(classFiles);
|
||||
}
|
||||
|
||||
public static ArchCondition<JavaClass> notCallMethodsInPackageUnless(final String packageIdentifier,
|
||||
final DescribedPredicate<JavaCall<?>>... unlessPredicates) {
|
||||
DescribedPredicate<JavaCall<?>> restrictedPackageCalls = target(
|
||||
HasOwner.Predicates.With.<JavaClass>owner(resideInAPackage(packageIdentifier)));
|
||||
|
||||
if (unlessPredicates.length > 0) {
|
||||
DescribedPredicate<JavaCall<?>> allowed = unlessPredicates[0];
|
||||
for (int x = 1; x < unlessPredicates.length; x++) {
|
||||
allowed = allowed.or(unlessPredicates[x]);
|
||||
}
|
||||
restrictedPackageCalls = unless(restrictedPackageCalls, allowed);
|
||||
}
|
||||
return not(callMethodWhere(restrictedPackageCalls));
|
||||
}
|
||||
|
||||
public static DescribedPredicate<JavaCall<?>> targetMethodIs(Class<?> owner,
|
||||
String methodName,
|
||||
Class<?>... parameterTypes) {
|
||||
return JavaCall.Predicates.target(owner(type(owner)))
|
||||
.and(JavaCall.Predicates.target(name(methodName)))
|
||||
.and(JavaCall.Predicates.target(rawParameterTypes(parameterTypes)))
|
||||
.as("method is %s",
|
||||
Formatters.formatMethodSimple(owner.getSimpleName(), methodName, namesOf(parameterTypes)));
|
||||
}
|
||||
|
||||
public static <T> DescribedPredicate<T> unless(DescribedPredicate<? super T> first,
|
||||
DescribedPredicate<? super T> second) {
|
||||
return new UnlessPredicate(first, second);
|
||||
}
|
||||
|
||||
private static class UnlessPredicate<T> extends DescribedPredicate<T> {
|
||||
private final DescribedPredicate<T> current;
|
||||
private final DescribedPredicate<? super T> other;
|
||||
|
||||
UnlessPredicate(DescribedPredicate<T> current, DescribedPredicate<? super T> other) {
|
||||
super(current.getDescription() + " unless " + other.getDescription());
|
||||
this.current = checkNotNull(current);
|
||||
this.other = checkNotNull(other);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(T input) {
|
||||
return current.apply(input) && !other.apply(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ public class BridgeMethodTest extends Assert {
|
||||
verifyBridgeMethods(GHIssue.class, "getCreatedAt", Date.class, String.class);
|
||||
verifyBridgeMethods(GHIssue.class, "getId", int.class, long.class, String.class);
|
||||
verifyBridgeMethods(GHIssue.class, "getUrl", String.class, URL.class);
|
||||
verifyBridgeMethods(GHIssue.class, "comment", 1, void.class, GHIssueComment.class);
|
||||
|
||||
verifyBridgeMethods(GHOrganization.class, "getHtmlUrl", String.class, URL.class);
|
||||
verifyBridgeMethods(GHOrganization.class, "getId", int.class, long.class, String.class);
|
||||
@@ -55,12 +56,17 @@ public class BridgeMethodTest extends Assert {
|
||||
}
|
||||
|
||||
void verifyBridgeMethods(@Nonnull Class<?> targetClass, @Nonnull String methodName, Class<?>... returnTypes) {
|
||||
verifyBridgeMethods(targetClass, methodName, 0, returnTypes);
|
||||
}
|
||||
|
||||
void verifyBridgeMethods(@Nonnull Class<?> targetClass,
|
||||
@Nonnull String methodName,
|
||||
int parameterCount,
|
||||
Class<?>... returnTypes) {
|
||||
List<Class<?>> foundMethods = new ArrayList<>();
|
||||
Method[] methods = targetClass.getMethods();
|
||||
for (Method method : methods) {
|
||||
if (method.getName().equalsIgnoreCase(methodName)) {
|
||||
// Bridge methods are only
|
||||
assertThat(method.getParameterCount(), equalTo(0));
|
||||
if (method.getName().equalsIgnoreCase(methodName) && method.getParameterCount() == parameterCount) {
|
||||
foundMethods.add(method.getReturnType());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,12 @@ import com.google.common.collect.Iterables;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
/**
|
||||
* @author Kohsuke Kawaguchi
|
||||
@@ -16,7 +18,7 @@ public class CommitTest extends AbstractGitHubWireMockTest {
|
||||
@Test // issue 152
|
||||
public void lastStatus() throws IOException {
|
||||
GHTag t = gitHub.getRepository("stapler/stapler").listTags().iterator().next();
|
||||
assertNotNull(t.getCommit().getLastStatus());
|
||||
assertThat(t.getCommit().getLastStatus(), notNullValue());
|
||||
}
|
||||
|
||||
@Test // issue 230
|
||||
@@ -25,10 +27,89 @@ public class CommitTest extends AbstractGitHubWireMockTest {
|
||||
PagedIterable<GHCommit> commits = repo.queryCommits().path("pom.xml").list();
|
||||
for (GHCommit commit : Iterables.limit(commits, 10)) {
|
||||
GHCommit expected = repo.getCommit(commit.getSHA1());
|
||||
assertEquals(expected.getFiles().size(), commit.getFiles().size());
|
||||
assertThat(commit.getFiles().size(), equalTo(expected.getFiles().size()));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryCommits() throws Exception {
|
||||
List<String> sha1 = new ArrayList<String>();
|
||||
List<GHCommit> commits = gitHub.getUser("jenkinsci")
|
||||
.getRepository("jenkins")
|
||||
.queryCommits()
|
||||
.since(1199174400000L)
|
||||
.until(1201852800000L)
|
||||
.path("pom.xml")
|
||||
.pageSize(100)
|
||||
.list()
|
||||
.toList();
|
||||
|
||||
assertThat(commits.get(0).getSHA1(), equalTo("1cccddb22e305397151b2b7b87b4b47d74ca337b"));
|
||||
assertThat(commits.size(), equalTo(29));
|
||||
|
||||
commits = gitHub.getUser("jenkinsci")
|
||||
.getRepository("jenkins")
|
||||
.queryCommits()
|
||||
.since(new Date(1199174400000L))
|
||||
.until(new Date(1201852800000L))
|
||||
.path("pom.xml")
|
||||
.pageSize(100)
|
||||
.list()
|
||||
.toList();
|
||||
|
||||
assertThat(commits.get(0).getSHA1(), equalTo("1cccddb22e305397151b2b7b87b4b47d74ca337b"));
|
||||
assertThat(commits.get(15).getSHA1(), equalTo("a5259970acaec9813e2a12a91f37dfc7871a5ef5"));
|
||||
assertThat(commits.size(), equalTo(29));
|
||||
|
||||
commits = gitHub.getUser("jenkinsci")
|
||||
.getRepository("jenkins")
|
||||
.queryCommits()
|
||||
.since(new Date(1199174400000L))
|
||||
.until(new Date(1201852800000L))
|
||||
.path("pom.xml")
|
||||
.from("a5259970acaec9813e2a12a91f37dfc7871a5ef5")
|
||||
.list()
|
||||
.toList();
|
||||
|
||||
assertThat(commits.get(0).getSHA1(), equalTo("a5259970acaec9813e2a12a91f37dfc7871a5ef5"));
|
||||
assertThat(commits.size(), equalTo(14));
|
||||
|
||||
commits = gitHub.getUser("jenkinsci")
|
||||
.getRepository("jenkins")
|
||||
.queryCommits()
|
||||
.until(new Date(1201852800000L))
|
||||
.path("pom.xml")
|
||||
.author("kohsuke")
|
||||
.list()
|
||||
.toList();
|
||||
|
||||
assertThat(commits, is(empty()));
|
||||
|
||||
commits = gitHub.getUser("jenkinsci")
|
||||
.getRepository("jenkins")
|
||||
.queryCommits()
|
||||
.until(new Date(1201852800000L))
|
||||
.path("pom.xml")
|
||||
.pageSize(100)
|
||||
.author("kohsuke@71c3de6d-444a-0410-be80-ed276b4c234a")
|
||||
.list()
|
||||
.toList();
|
||||
|
||||
assertThat(commits.size(), equalTo(266));
|
||||
|
||||
commits = gitHub.getUser("jenkinsci")
|
||||
.getRepository("jenkins")
|
||||
.queryCommits()
|
||||
.path("pom.xml")
|
||||
.pageSize(100)
|
||||
.author("kohsuke@71c3de6d-444a-0410-be80-ed276b4c234a")
|
||||
.list()
|
||||
.toList();
|
||||
|
||||
assertThat(commits.size(), equalTo(648));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listPullRequestsOfNotIncludedCommit() throws Exception {
|
||||
GHRepository repo = gitHub.getOrganization("hub4j-test-org").getRepository("listPrsListHeads");
|
||||
@@ -48,7 +129,7 @@ public class CommitTest extends AbstractGitHubWireMockTest {
|
||||
|
||||
List<GHPullRequest> listedPrs = commit.listPullRequests().toList();
|
||||
|
||||
assertEquals(listedPrs.size(), 1);
|
||||
assertThat(1, equalTo(listedPrs.size()));
|
||||
|
||||
assertThat("Pull request " + prNumber + " not found by searching from commit.",
|
||||
listedPrs.stream().findFirst().filter(it -> it.getNumber() == prNumber).isPresent());
|
||||
@@ -63,7 +144,7 @@ public class CommitTest extends AbstractGitHubWireMockTest {
|
||||
|
||||
List<GHPullRequest> listedPrs = commit.listPullRequests().toList();
|
||||
|
||||
assertEquals(listedPrs.size(), 2);
|
||||
assertThat(2, equalTo(listedPrs.size()));
|
||||
|
||||
listedPrs.stream()
|
||||
.forEach(pr -> assertThat("PR#" + pr.getNumber() + " not expected to be matched.",
|
||||
@@ -76,12 +157,12 @@ public class CommitTest extends AbstractGitHubWireMockTest {
|
||||
|
||||
GHCommit commit = repo.getCommit("ab92e13c0fc844fd51a379a48a3ad0b18231215c");
|
||||
|
||||
assertThat("Commit which was supposed to be HEAD in the \"master\" branch was not found.",
|
||||
assertThat("Commit which was supposed to be HEAD in the \"main\" branch was not found.",
|
||||
commit.listBranchesWhereHead()
|
||||
.toList()
|
||||
.stream()
|
||||
.findFirst()
|
||||
.filter(it -> it.getName().equals("master"))
|
||||
.filter(it -> it.getName().equals("main"))
|
||||
.isPresent());
|
||||
}
|
||||
|
||||
@@ -91,9 +172,9 @@ public class CommitTest extends AbstractGitHubWireMockTest {
|
||||
|
||||
GHCommit commit = repo.getCommit("ab92e13c0fc844fd51a379a48a3ad0b18231215c");
|
||||
|
||||
assertEquals("Commit which was supposed to be HEAD in 2 branches was not found as such.",
|
||||
2,
|
||||
commit.listBranchesWhereHead().toList().size());
|
||||
assertThat("Commit which was supposed to be HEAD in 2 branches was not found as such.",
|
||||
commit.listBranchesWhereHead().toList().size(),
|
||||
equalTo(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -112,14 +193,14 @@ public class CommitTest extends AbstractGitHubWireMockTest {
|
||||
PagedIterable<GHCommit> commits = repo.queryCommits().path("pom.xml").list();
|
||||
for (GHCommit commit : Iterables.limit(commits, 10)) {
|
||||
GHCommit expected = repo.getCommit(commit.getSHA1());
|
||||
assertEquals(expected.getCommitShortInfo().getVerification().isVerified(),
|
||||
commit.getCommitShortInfo().getVerification().isVerified());
|
||||
assertEquals(expected.getCommitShortInfo().getVerification().getReason(),
|
||||
commit.getCommitShortInfo().getVerification().getReason());
|
||||
assertEquals(expected.getCommitShortInfo().getVerification().getSignature(),
|
||||
commit.getCommitShortInfo().getVerification().getSignature());
|
||||
assertEquals(expected.getCommitShortInfo().getVerification().getPayload(),
|
||||
commit.getCommitShortInfo().getVerification().getPayload());
|
||||
assertThat(commit.getCommitShortInfo().getVerification().isVerified(),
|
||||
equalTo(expected.getCommitShortInfo().getVerification().isVerified()));
|
||||
assertThat(commit.getCommitShortInfo().getVerification().getReason(),
|
||||
equalTo(expected.getCommitShortInfo().getVerification().getReason()));
|
||||
assertThat(commit.getCommitShortInfo().getVerification().getSignature(),
|
||||
equalTo(expected.getCommitShortInfo().getVerification().getSignature()));
|
||||
assertThat(commit.getCommitShortInfo().getVerification().getPayload(),
|
||||
equalTo(expected.getCommitShortInfo().getVerification().getPayload()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
85
src/test/java/org/kohsuke/github/EnumTest.java
Normal file
85
src/test/java/org/kohsuke/github/EnumTest.java
Normal file
@@ -0,0 +1,85 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
|
||||
/**
|
||||
* Unit test for {@link GitHub} static helpers.
|
||||
*
|
||||
* @author Liam Newman
|
||||
*/
|
||||
public class EnumTest extends AbstractGitHubWireMockTest {
|
||||
|
||||
@Test
|
||||
public void touchEnums() {
|
||||
assertThat(GHCheckRun.AnnotationLevel.values().length, equalTo(3));
|
||||
assertThat(GHCheckRun.Conclusion.values().length, equalTo(9));
|
||||
assertThat(GHCheckRun.Status.values().length, equalTo(4));
|
||||
|
||||
assertThat(GHCommentAuthorAssociation.values().length, equalTo(7));
|
||||
|
||||
assertThat(GHCommitState.values().length, equalTo(4));
|
||||
|
||||
assertThat(GHCompare.Status.values().length, equalTo(4));
|
||||
|
||||
assertThat(GHDeploymentState.values().length, equalTo(7));
|
||||
|
||||
assertThat(GHDirection.values().length, equalTo(2));
|
||||
|
||||
assertThat(GHEvent.values().length, equalTo(60));
|
||||
assertThat(GHEvent.ALL.symbol(), equalTo("*"));
|
||||
assertThat(GHEvent.PULL_REQUEST.symbol(), equalTo(GHEvent.PULL_REQUEST.toString().toLowerCase()));
|
||||
|
||||
assertThat(GHIssueSearchBuilder.Sort.values().length, equalTo(3));
|
||||
|
||||
assertThat(GHIssueState.values().length, equalTo(3));
|
||||
|
||||
assertThat(GHMarketplaceAccountType.values().length, equalTo(2));
|
||||
|
||||
assertThat(GHMarketplaceListAccountBuilder.Sort.values().length, equalTo(2));
|
||||
|
||||
assertThat(GHMarketplacePriceModel.values().length, equalTo(3));
|
||||
|
||||
assertThat(GHMembership.Role.values().length, equalTo(2));
|
||||
|
||||
assertThat(GHMilestoneState.values().length, equalTo(2));
|
||||
|
||||
assertThat(GHMyself.RepositoryListFilter.values().length, equalTo(5));
|
||||
|
||||
assertThat(GHOrganization.Role.values().length, equalTo(2));
|
||||
assertThat(GHOrganization.Permission.values().length, equalTo(5));
|
||||
|
||||
assertThat(GHPermissionType.values().length, equalTo(4));
|
||||
|
||||
assertThat(GHProject.ProjectState.values().length, equalTo(2));
|
||||
assertThat(GHProject.ProjectStateFilter.values().length, equalTo(3));
|
||||
|
||||
assertThat(GHPullRequest.MergeMethod.values().length, equalTo(3));
|
||||
|
||||
assertThat(GHPullRequestQueryBuilder.Sort.values().length, equalTo(4));
|
||||
|
||||
assertThat(GHPullRequestReviewEvent.values().length, equalTo(4));
|
||||
assertThat(GHPullRequestReviewEvent.PENDING.toState(), equalTo(GHPullRequestReviewState.PENDING));
|
||||
assertThat(GHPullRequestReviewEvent.PENDING.action(), nullValue());
|
||||
|
||||
assertThat(GHPullRequestReviewState.values().length, equalTo(6));
|
||||
assertThat(GHPullRequestReviewState.PENDING.toEvent(), equalTo(GHPullRequestReviewEvent.PENDING));
|
||||
assertThat(GHPullRequestReviewState.APPROVED.action(), equalTo(GHPullRequestReviewEvent.APPROVE.action()));
|
||||
assertThat(GHPullRequestReviewState.DISMISSED.toEvent(), nullValue());
|
||||
|
||||
assertThat(GHRepository.CollaboratorAffiliation.values().length, equalTo(3));
|
||||
assertThat(GHRepository.ForkSort.values().length, equalTo(3));
|
||||
assertThat(GHRepository.Visibility.values().length, equalTo(4));
|
||||
|
||||
assertThat(GHRepositorySearchBuilder.Sort.values().length, equalTo(3));
|
||||
|
||||
assertThat(GHRepositorySelection.values().length, equalTo(2));
|
||||
|
||||
assertThat(GHTeam.Role.values().length, equalTo(2));
|
||||
assertThat(GHTeam.Privacy.values().length, equalTo(2));
|
||||
|
||||
assertThat(GHUserSearchBuilder.Sort.values().length, equalTo(3));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,6 +5,8 @@ import org.junit.Test;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
public class GHAppInstallationTest extends AbstractGHAppInstallationTest {
|
||||
|
||||
@Test
|
||||
@@ -13,16 +15,16 @@ public class GHAppInstallationTest extends AbstractGHAppInstallationTest {
|
||||
|
||||
List<GHRepository> repositories = appInstallation.listRepositories().toList();
|
||||
|
||||
assertEquals(2, repositories.size());
|
||||
assertTrue(repositories.stream().anyMatch(it -> it.getName().equals("empty")));
|
||||
assertTrue(repositories.stream().anyMatch(it -> it.getName().equals("test-readme")));
|
||||
assertThat(repositories.size(), equalTo(2));
|
||||
assertThat(repositories.stream().map(GHRepository::getName).toArray(),
|
||||
arrayContainingInAnyOrder("empty", "test-readme"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListRepositoriesNoPermissions() throws IOException {
|
||||
GHAppInstallation appInstallation = getAppInstallationWithTokenApp2();
|
||||
|
||||
assertTrue("App does not have permissions and should have 0 repositories",
|
||||
assertThat("App does not have permissions and should have 0 repositories",
|
||||
appInstallation.listRepositories().toList().isEmpty());
|
||||
}
|
||||
|
||||
|
||||
@@ -103,7 +103,8 @@ public class GHAppTest extends AbstractGitHubWireMockTest {
|
||||
permissions.put("metadata", GHPermissionType.READ);
|
||||
|
||||
// Create token specifying both permissions and repository ids
|
||||
GHAppInstallationToken installationToken = installation.createToken(permissions)
|
||||
GHAppInstallationToken installationToken = installation.createToken()
|
||||
.permissions(permissions)
|
||||
.repositoryIds(Collections.singletonList((long) 111111111))
|
||||
.create();
|
||||
|
||||
@@ -125,7 +126,7 @@ public class GHAppTest extends AbstractGitHubWireMockTest {
|
||||
assertThat(installationToken2.getRepositorySelection(), is(GHRepositorySelection.ALL));
|
||||
assertThat(installationToken2.getExpiresAt(), is(GitHubClient.parseDate("2019-12-19T12:27:59Z")));
|
||||
|
||||
assertNull(installationToken2.getRepositories());;
|
||||
assertThat(installationToken2.getRepositories(), nullValue());;
|
||||
}
|
||||
|
||||
private void testAppInstallation(GHAppInstallation appInstallation) throws IOException {
|
||||
@@ -153,7 +154,7 @@ public class GHAppTest extends AbstractGitHubWireMockTest {
|
||||
assertThat(appInstallation.getEvents(), containsInAnyOrder(events.toArray(new GHEvent[0])));
|
||||
assertThat(appInstallation.getCreatedAt(), is(GitHubClient.parseDate("2019-07-04T01:19:36.000Z")));
|
||||
assertThat(appInstallation.getUpdatedAt(), is(GitHubClient.parseDate("2019-07-30T22:48:09.000Z")));
|
||||
assertNull(appInstallation.getSingleFileName());
|
||||
assertThat(appInstallation.getSingleFileName(), nullValue());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,10 +6,10 @@ import org.kohsuke.github.GHBranchProtection.EnforceAdmins;
|
||||
import org.kohsuke.github.GHBranchProtection.RequiredReviews;
|
||||
import org.kohsuke.github.GHBranchProtection.RequiredStatusChecks;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
public class GHBranchProtectionTest extends AbstractGitHubWireMockTest {
|
||||
private static final String BRANCH = "master";
|
||||
private static final String BRANCH = "main";
|
||||
private static final String BRANCH_REF = "heads/" + BRANCH;
|
||||
|
||||
private GHBranch branch;
|
||||
@@ -43,33 +43,33 @@ public class GHBranchProtectionTest extends AbstractGitHubWireMockTest {
|
||||
|
||||
private void verifyBranchProtection(GHBranchProtection protection) {
|
||||
RequiredStatusChecks statusChecks = protection.getRequiredStatusChecks();
|
||||
assertNotNull(statusChecks);
|
||||
assertTrue(statusChecks.isRequiresBranchUpToDate());
|
||||
assertTrue(statusChecks.getContexts().contains("test-status-check"));
|
||||
assertThat(statusChecks, notNullValue());
|
||||
assertThat(statusChecks.isRequiresBranchUpToDate(), is(true));
|
||||
assertThat(statusChecks.getContexts(), contains("test-status-check"));
|
||||
|
||||
RequiredReviews requiredReviews = protection.getRequiredReviews();
|
||||
assertNotNull(requiredReviews);
|
||||
assertTrue(requiredReviews.isDismissStaleReviews());
|
||||
assertTrue(requiredReviews.isRequireCodeOwnerReviews());
|
||||
assertEquals(2, requiredReviews.getRequiredReviewers());
|
||||
assertThat(requiredReviews, notNullValue());
|
||||
assertThat(requiredReviews.isDismissStaleReviews(), is(true));
|
||||
assertThat(requiredReviews.isRequireCodeOwnerReviews(), is(true));
|
||||
assertThat(requiredReviews.getRequiredReviewers(), equalTo(2));
|
||||
|
||||
EnforceAdmins enforceAdmins = protection.getEnforceAdmins();
|
||||
assertNotNull(enforceAdmins);
|
||||
assertTrue(enforceAdmins.isEnabled());
|
||||
assertThat(enforceAdmins, notNullValue());
|
||||
assertThat(enforceAdmins.isEnabled(), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEnableProtectionOnly() throws Exception {
|
||||
branch.enableProtection().enable();
|
||||
assertTrue(repo.getBranch(BRANCH).isProtected());
|
||||
assertThat(repo.getBranch(BRANCH).isProtected(), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDisableProtectionOnly() throws Exception {
|
||||
GHBranchProtection protection = branch.enableProtection().enable();
|
||||
assertTrue(repo.getBranch(BRANCH).isProtected());
|
||||
assertThat(repo.getBranch(BRANCH).isProtected(), is(true));
|
||||
branch.disableProtection();
|
||||
assertFalse(repo.getBranch(BRANCH).isProtected());
|
||||
assertThat(repo.getBranch(BRANCH).isProtected(), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -77,18 +77,18 @@ public class GHBranchProtectionTest extends AbstractGitHubWireMockTest {
|
||||
GHBranchProtection protection = branch.enableProtection().requireReviews().enable();
|
||||
|
||||
RequiredReviews requiredReviews = protection.getRequiredReviews();
|
||||
assertNotNull(protection.getRequiredReviews());
|
||||
assertFalse(requiredReviews.isDismissStaleReviews());
|
||||
assertFalse(requiredReviews.isRequireCodeOwnerReviews());
|
||||
assertThat(protection.getRequiredReviews(), notNullValue());
|
||||
assertThat(requiredReviews.isDismissStaleReviews(), is(false));
|
||||
assertThat(requiredReviews.isRequireCodeOwnerReviews(), is(false));
|
||||
assertThat(protection.getRequiredReviews().getRequiredReviewers(), equalTo(1));
|
||||
|
||||
// Get goes through a different code path. Make sure it also gets the correct data.
|
||||
protection = branch.getProtection();
|
||||
requiredReviews = protection.getRequiredReviews();
|
||||
|
||||
assertNotNull(protection.getRequiredReviews());
|
||||
assertFalse(requiredReviews.isDismissStaleReviews());
|
||||
assertFalse(requiredReviews.isRequireCodeOwnerReviews());
|
||||
assertThat(protection.getRequiredReviews(), notNullValue());
|
||||
assertThat(requiredReviews.isDismissStaleReviews(), is(false));
|
||||
assertThat(requiredReviews.isRequireCodeOwnerReviews(), is(false));
|
||||
assertThat(protection.getRequiredReviews().getRequiredReviewers(), equalTo(1));
|
||||
}
|
||||
|
||||
@@ -96,20 +96,21 @@ public class GHBranchProtectionTest extends AbstractGitHubWireMockTest {
|
||||
public void testSignedCommits() throws Exception {
|
||||
GHBranchProtection protection = branch.enableProtection().enable();
|
||||
|
||||
assertFalse(protection.getRequiredSignatures());
|
||||
assertThat(protection.getRequiredSignatures(), is(false));
|
||||
|
||||
protection.enabledSignedCommits();
|
||||
assertTrue(protection.getRequiredSignatures());
|
||||
assertThat(protection.getRequiredSignatures(), is(true));
|
||||
|
||||
protection.disableSignedCommits();
|
||||
assertFalse(protection.getRequiredSignatures());
|
||||
assertThat(protection.getRequiredSignatures(), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetProtection() throws Exception {
|
||||
GHBranchProtection protection = branch.enableProtection().enable();
|
||||
GHBranchProtection protectionTest = repo.getBranch(BRANCH).getProtection();
|
||||
assertTrue(protectionTest instanceof GHBranchProtection);
|
||||
assertTrue(repo.getBranch(BRANCH).isProtected());
|
||||
Boolean condition = protectionTest instanceof GHBranchProtection;
|
||||
assertThat(protectionTest, instanceOf(GHBranchProtection.class));
|
||||
assertThat(repo.getBranch(BRANCH).isProtected(), is(true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,9 @@ public class GHBranchTest extends AbstractGitHubWireMockTest {
|
||||
public void testMergeBranch() throws Exception {
|
||||
repository = getTempRepository();
|
||||
|
||||
String masterHead = repository.getRef("heads/master").getObject().getSha();
|
||||
createRefAndPostContent(BRANCH_1, masterHead);
|
||||
createRefAndPostContent(BRANCH_2, masterHead);
|
||||
String mainHead = repository.getRef("heads/main").getObject().getSha();
|
||||
createRefAndPostContent(BRANCH_1, mainHead);
|
||||
createRefAndPostContent(BRANCH_2, mainHead);
|
||||
|
||||
GHBranch otherBranch = repository.getBranch(BRANCH_2);
|
||||
String commitMessage = "merging " + BRANCH_2;
|
||||
@@ -28,13 +28,13 @@ public class GHBranchTest extends AbstractGitHubWireMockTest {
|
||||
|
||||
// Merging commit sha should work
|
||||
commitMessage = "merging from " + mergeCommit.getSHA1();
|
||||
GHBranch master = repository.getBranch("master");
|
||||
mergeCommit = master.merge(mergeCommit.getSHA1(), commitMessage);
|
||||
GHBranch main = repository.getBranch("main");
|
||||
mergeCommit = main.merge(mergeCommit.getSHA1(), commitMessage);
|
||||
|
||||
assertThat(mergeCommit, notNullValue());
|
||||
assertThat(mergeCommit.getCommitShortInfo().getMessage(), equalTo(commitMessage));
|
||||
|
||||
mergeCommit = master.merge(mergeCommit.getSHA1(), commitMessage);
|
||||
mergeCommit = main.merge(mergeCommit.getSHA1(), commitMessage);
|
||||
// Should be null since all changes already merged
|
||||
assertThat(mergeCommit, nullValue());
|
||||
}
|
||||
|
||||
@@ -25,11 +25,12 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.kohsuke.github.GHCheckRun.Status;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
@SuppressWarnings("deprecation") // preview
|
||||
public class GHCheckRunBuilderTest extends AbstractGHAppInstallationTest {
|
||||
@@ -48,7 +49,7 @@ public class GHCheckRunBuilderTest extends AbstractGHAppInstallationTest {
|
||||
.withExternalID("whatever")
|
||||
.withStartedAt(new Date(999_999_000))
|
||||
.withCompletedAt(new Date(999_999_999))
|
||||
.add(new GHCheckRunBuilder.Output("Some Title", "what happened…")
|
||||
.add(new GHCheckRunBuilder.Output("Some Title", "what happened…").withText("Hello Text!")
|
||||
.add(new GHCheckRunBuilder.Annotation("stuff.txt",
|
||||
1,
|
||||
GHCheckRun.AnnotationLevel.NOTICE,
|
||||
@@ -58,14 +59,17 @@ public class GHCheckRunBuilderTest extends AbstractGHAppInstallationTest {
|
||||
.withCaption("Princess Unikitty")))
|
||||
.add(new GHCheckRunBuilder.Action("Help", "what I need help with", "doit"))
|
||||
.create();
|
||||
assertEquals("completed", checkRun.getStatus());
|
||||
assertEquals(1, checkRun.getOutput().getAnnotationsCount());
|
||||
assertEquals(1424883286, checkRun.getId());
|
||||
assertThat(checkRun.getStatus(), equalTo(Status.COMPLETED));
|
||||
assertThat(checkRun.getOutput().getAnnotationsCount(), equalTo(1));
|
||||
assertThat(checkRun.getId(), equalTo(1424883286L));
|
||||
assertThat(checkRun.getOutput().getText(), equalTo("Hello Text!"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createCheckRunManyAnnotations() throws Exception {
|
||||
GHCheckRunBuilder.Output output = new GHCheckRunBuilder.Output("Big Run", "Lots of stuff here »");
|
||||
GHCheckRunBuilder.Output output = new GHCheckRunBuilder.Output("Big Run", "Lots of stuff here »")
|
||||
.withText("Hello Text!");
|
||||
|
||||
for (int i = 0; i < 101; i++) {
|
||||
output.add(
|
||||
new GHCheckRunBuilder.Annotation("stuff.txt", 1, GHCheckRun.AnnotationLevel.NOTICE, "hello #" + i));
|
||||
@@ -75,11 +79,12 @@ public class GHCheckRunBuilderTest extends AbstractGHAppInstallationTest {
|
||||
.withConclusion(GHCheckRun.Conclusion.SUCCESS)
|
||||
.add(output)
|
||||
.create();
|
||||
assertEquals("completed", checkRun.getStatus());
|
||||
assertEquals("Big Run", checkRun.getOutput().getTitle());
|
||||
assertEquals("Lots of stuff here »", checkRun.getOutput().getSummary());
|
||||
assertEquals(101, checkRun.getOutput().getAnnotationsCount());
|
||||
assertEquals(1424883599, checkRun.getId());
|
||||
assertThat(checkRun.getStatus(), equalTo(Status.COMPLETED));
|
||||
assertThat(checkRun.getOutput().getTitle(), equalTo("Big Run"));
|
||||
assertThat(checkRun.getOutput().getSummary(), equalTo("Lots of stuff here »"));
|
||||
assertThat(checkRun.getOutput().getAnnotationsCount(), equalTo(101));
|
||||
assertThat(checkRun.getOutput().getText(), equalTo("Hello Text!"));
|
||||
assertThat(checkRun.getId(), equalTo(1424883599L));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -89,9 +94,9 @@ public class GHCheckRunBuilderTest extends AbstractGHAppInstallationTest {
|
||||
.withConclusion(GHCheckRun.Conclusion.NEUTRAL)
|
||||
.add(new GHCheckRunBuilder.Output("Quick note", "nothing more to see here"))
|
||||
.create();
|
||||
assertEquals("completed", checkRun.getStatus());
|
||||
assertEquals(0, checkRun.getOutput().getAnnotationsCount());
|
||||
assertEquals(1424883957, checkRun.getId());
|
||||
assertThat(checkRun.getStatus(), equalTo(Status.COMPLETED));
|
||||
assertThat(checkRun.getOutput().getAnnotationsCount(), equalTo(0));
|
||||
assertThat(checkRun.getId(), equalTo(1424883957L));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -100,9 +105,9 @@ public class GHCheckRunBuilderTest extends AbstractGHAppInstallationTest {
|
||||
.createCheckRun("outstanding", "89a9ae301e35e667756034fdc933b1fc94f63fc1")
|
||||
.withStatus(GHCheckRun.Status.IN_PROGRESS)
|
||||
.create();
|
||||
assertEquals("in_progress", checkRun.getStatus());
|
||||
assertNull(checkRun.getConclusion());
|
||||
assertEquals(1424883451, checkRun.getId());
|
||||
assertThat(checkRun.getStatus(), equalTo(Status.IN_PROGRESS));
|
||||
assertThat(checkRun.getConclusion(), nullValue());
|
||||
assertThat(checkRun.getId(), equalTo(1424883451L));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -114,8 +119,10 @@ public class GHCheckRunBuilderTest extends AbstractGHAppInstallationTest {
|
||||
.create();
|
||||
fail("should have been rejected");
|
||||
} catch (HttpException x) {
|
||||
assertEquals(422, x.getResponseCode());
|
||||
assertThat(x.getResponseCode(), equalTo(422));
|
||||
assertThat(x.getMessage(), containsString("\\\"conclusion\\\" wasn't supplied"));
|
||||
assertThat(x.getUrl(), containsString("/repos/hub4j-test-org/test-checks/check-runs"));
|
||||
assertThat(x.getResponseMessage(), equalTo("422 Unprocessable Entity"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,9 +143,9 @@ public class GHCheckRunBuilderTest extends AbstractGHAppInstallationTest {
|
||||
.withConclusion(GHCheckRun.Conclusion.SUCCESS)
|
||||
.withCompletedAt(new Date(999_999_999))
|
||||
.create();
|
||||
assertEquals(updated.getStartedAt(), new Date(999_999_000));
|
||||
assertEquals(updated.getName(), "foo");
|
||||
assertEquals(1, checkRun.getOutput().getAnnotationsCount());
|
||||
assertThat(new Date(999_999_000), equalTo(updated.getStartedAt()));
|
||||
assertThat("foo", equalTo(updated.getName()));
|
||||
assertThat(checkRun.getOutput().getAnnotationsCount(), equalTo(1));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,8 +11,7 @@ import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.Matchers.hasProperty;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
/**
|
||||
* Integration test for {@link GHContent}.
|
||||
@@ -29,7 +28,7 @@ public class GHContentIntegrationTest extends AbstractGitHubWireMockTest {
|
||||
@After
|
||||
public void cleanup() throws Exception {
|
||||
if (mockGitHub.isUseProxy()) {
|
||||
repo = getGitHubBeforeAfter().getRepository("hub4j-test-org/GHContentIntegrationTest");
|
||||
repo = getNonRecordingGitHub().getRepository("hub4j-test-org/GHContentIntegrationTest");
|
||||
try {
|
||||
GHContent content = repo.getFileContent(createdFilename);
|
||||
if (content != null) {
|
||||
@@ -45,36 +44,44 @@ public class GHContentIntegrationTest extends AbstractGitHubWireMockTest {
|
||||
repo = gitHub.getRepository("hub4j-test-org/GHContentIntegrationTest");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRepository() throws Exception {
|
||||
GHRepository testRepo = gitHub.getRepositoryById(repo.getId());
|
||||
assertThat(testRepo.getName(), equalTo(repo.getName()));
|
||||
testRepo = gitHub.getRepositoryById(Long.toString(repo.getId()));
|
||||
assertThat(testRepo.getName(), equalTo(repo.getName()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetFileContent() throws Exception {
|
||||
repo = gitHub.getRepository("hub4j-test-org/GHContentIntegrationTest");
|
||||
GHContent content = repo.getFileContent("ghcontent-ro/a-file-with-content");
|
||||
|
||||
assertTrue(content.isFile());
|
||||
assertEquals("thanks for reading me\n", content.getContent());
|
||||
assertThat(content.isFile(), is(true));
|
||||
assertThat(content.getContent(), equalTo("thanks for reading me\n"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetEmptyFileContent() throws Exception {
|
||||
GHContent content = repo.getFileContent("ghcontent-ro/an-empty-file");
|
||||
|
||||
assertTrue(content.isFile());
|
||||
assertEquals("", content.getContent());
|
||||
assertThat(content.isFile(), is(true));
|
||||
assertThat(content.getContent(), is(emptyString()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDirectoryContent() throws Exception {
|
||||
List<GHContent> entries = repo.getDirectoryContent("ghcontent-ro/a-dir-with-3-entries");
|
||||
|
||||
assertTrue(entries.size() == 3);
|
||||
assertThat(entries.size(), equalTo(3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDirectoryContentTrailingSlash() throws Exception {
|
||||
// Used to truncate the ?ref=master, see gh-224 https://github.com/kohsuke/github-api/pull/224
|
||||
List<GHContent> entries = repo.getDirectoryContent("ghcontent-ro/a-dir-with-3-entries/", "master");
|
||||
// Used to truncate the ?ref=main, see gh-224 https://github.com/kohsuke/github-api/pull/224
|
||||
List<GHContent> entries = repo.getDirectoryContent("ghcontent-ro/a-dir-with-3-entries/", "main");
|
||||
|
||||
assertTrue(entries.get(0).getUrl().endsWith("?ref=master"));
|
||||
assertThat(entries.get(0).getUrl(), endsWith("?ref=main"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -84,11 +91,11 @@ public class GHContentIntegrationTest extends AbstractGitHubWireMockTest {
|
||||
createdFilename);
|
||||
GHContent createdContent = created.getContent();
|
||||
|
||||
assertNotNull(created.getCommit());
|
||||
assertNotNull(created.getContent());
|
||||
assertNotNull(createdContent.getContent());
|
||||
assertThat(created.getCommit(), notNullValue());
|
||||
assertThat(created.getContent(), notNullValue());
|
||||
assertThat(createdContent.getContent(), notNullValue());
|
||||
assertThat(createdContent.getPath(), equalTo(createdFilename));
|
||||
assertEquals("this is an awesome file I created\n", createdContent.getContent());
|
||||
assertThat(createdContent.getContent(), equalTo("this is an awesome file I created\n"));
|
||||
|
||||
GHContent content = repo.getFileContent(createdFilename);
|
||||
assertThat(content, is(notNullValue()));
|
||||
@@ -108,17 +115,17 @@ public class GHContentIntegrationTest extends AbstractGitHubWireMockTest {
|
||||
"Updated file for integration tests.");
|
||||
GHContent updatedContent = updatedContentResponse.getContent();
|
||||
|
||||
assertNotNull(updatedContentResponse.getCommit());
|
||||
assertNotNull(updatedContentResponse.getContent());
|
||||
assertThat(updatedContentResponse.getCommit(), notNullValue());
|
||||
assertThat(updatedContentResponse.getContent(), notNullValue());
|
||||
// due to what appears to be a cache propagation delay, this test is too flaky
|
||||
assertEquals("this is some new content",
|
||||
new BufferedReader(new InputStreamReader(updatedContent.read())).readLine());
|
||||
assertEquals("this is some new content\n", updatedContent.getContent());
|
||||
assertThat(new BufferedReader(new InputStreamReader(updatedContent.read())).readLine(),
|
||||
equalTo("this is some new content"));
|
||||
assertThat(updatedContent.getContent(), equalTo("this is some new content\n"));
|
||||
|
||||
GHContentUpdateResponse deleteResponse = updatedContent.delete("Enough of this foolishness!");
|
||||
|
||||
assertNotNull(deleteResponse.getCommit());
|
||||
assertNull(deleteResponse.getContent());
|
||||
assertThat(deleteResponse.getCommit(), notNullValue());
|
||||
assertThat(deleteResponse.getContent(), nullValue());
|
||||
|
||||
try {
|
||||
repo.getFileContent(createdFilename);
|
||||
|
||||
@@ -6,6 +6,8 @@ import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
/**
|
||||
* @author Martin van Zijl
|
||||
*/
|
||||
@@ -15,38 +17,38 @@ public class GHDeploymentTest extends AbstractGitHubWireMockTest {
|
||||
public void testGetDeploymentByIdStringPayload() throws IOException {
|
||||
final GHRepository repo = getRepository();
|
||||
final GHDeployment deployment = repo.getDeployment(178653229);
|
||||
assertNotNull(deployment);
|
||||
assertEquals(178653229, deployment.getId());
|
||||
assertEquals("production", deployment.getEnvironment());
|
||||
assertEquals("custom", deployment.getPayload());
|
||||
assertEquals("custom", deployment.getPayloadObject());
|
||||
assertEquals("master", deployment.getRef());
|
||||
assertEquals("3a09d2de4a9a1322a0ba2c3e2f54a919ca8fe353", deployment.getSha());
|
||||
assertEquals("deploy", deployment.getTask());
|
||||
assertEquals("production", deployment.getOriginalEnvironment());
|
||||
assertEquals(false, deployment.isProductionEnvironment());
|
||||
assertEquals(true, deployment.isTransientEnvironment());
|
||||
assertThat(deployment, notNullValue());
|
||||
assertThat(deployment.getId(), equalTo(178653229L));
|
||||
assertThat(deployment.getEnvironment(), equalTo("production"));
|
||||
assertThat(deployment.getPayload(), equalTo("custom"));
|
||||
assertThat(deployment.getPayloadObject(), equalTo("custom"));
|
||||
assertThat(deployment.getRef(), equalTo("main"));
|
||||
assertThat(deployment.getSha(), equalTo("3a09d2de4a9a1322a0ba2c3e2f54a919ca8fe353"));
|
||||
assertThat(deployment.getTask(), equalTo("deploy"));
|
||||
assertThat(deployment.getOriginalEnvironment(), equalTo("production"));
|
||||
assertThat(deployment.isProductionEnvironment(), equalTo(false));
|
||||
assertThat(deployment.isTransientEnvironment(), equalTo(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDeploymentByIdObjectPayload() throws IOException {
|
||||
final GHRepository repo = getRepository();
|
||||
final GHDeployment deployment = repo.getDeployment(178653229);
|
||||
assertNotNull(deployment);
|
||||
assertEquals(178653229, deployment.getId());
|
||||
assertEquals("production", deployment.getEnvironment());
|
||||
assertEquals("master", deployment.getRef());
|
||||
assertEquals("3a09d2de4a9a1322a0ba2c3e2f54a919ca8fe353", deployment.getSha());
|
||||
assertEquals("deploy", deployment.getTask());
|
||||
assertThat(deployment, notNullValue());
|
||||
assertThat(deployment.getId(), equalTo(178653229L));
|
||||
assertThat(deployment.getEnvironment(), equalTo("production"));
|
||||
assertThat(deployment.getRef(), equalTo("main"));
|
||||
assertThat(deployment.getSha(), equalTo("3a09d2de4a9a1322a0ba2c3e2f54a919ca8fe353"));
|
||||
assertThat(deployment.getTask(), equalTo("deploy"));
|
||||
final Map<String, Object> payload = deployment.getPayloadMap();
|
||||
assertEquals(4, payload.size());
|
||||
assertEquals(1, payload.get("custom1"));
|
||||
assertEquals("two", payload.get("custom2"));
|
||||
assertEquals(Arrays.asList("3", 3, "three"), payload.get("custom3"));
|
||||
assertNull(payload.get("custom4"));
|
||||
assertEquals("production", deployment.getOriginalEnvironment());
|
||||
assertEquals(false, deployment.isProductionEnvironment());
|
||||
assertEquals(true, deployment.isTransientEnvironment());
|
||||
assertThat(payload.size(), equalTo(4));
|
||||
assertThat(payload.get("custom1"), equalTo(1));
|
||||
assertThat(payload.get("custom2"), equalTo("two"));
|
||||
assertThat(payload.get("custom3"), equalTo(Arrays.asList("3", 3, "three")));
|
||||
assertThat(payload.get("custom4"), nullValue());
|
||||
assertThat(deployment.getOriginalEnvironment(), equalTo("production"));
|
||||
assertThat(deployment.isProductionEnvironment(), equalTo(false));
|
||||
assertThat(deployment.isTransientEnvironment(), equalTo(true));
|
||||
}
|
||||
|
||||
protected GHRepository getRepository() throws IOException {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user