Unvendor Archery (#459)
diff --git a/.github/workflows/comment_bot.yml b/.github/workflows/comment_bot.yml
index 9e10300..6ca0953 100644
--- a/.github/workflows/comment_bot.yml
+++ b/.github/workflows/comment_bot.yml
@@ -33,13 +33,13 @@
- name: Checkout Arrow
uses: actions/checkout@v2
with:
- path: arrow
+ repository: apache/arrow
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.8
- name: Install Archery and Crossbow dependencies
- run: pip install -e arrow/dev/archery[bot]
+ run: pip install -e dev/archery[bot]
- name: Handle Github comment event
env:
ARROW_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -49,78 +49,6 @@
--event-name ${{ github.event_name }} \
--event-payload ${{ github.event_path }}
- autotune:
- name: "Fix all the things"
- if: startsWith(github.event.comment.body, '@github-actions autotune')
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v2
- - uses: r-lib/actions/pr-fetch@master
- with:
- repo-token: ${{ secrets.GITHUB_TOKEN }}
- - name: See what is different
- run: |
- set -ex
- git remote add upstream https://github.com/apache/arrow
- git fetch upstream
- changed() {
- git diff --name-only HEAD..upstream/master | grep -e "$1" >/dev/null 2>&1
- }
- if changed '^r/.*\.R$'; then
- echo "R_DOCS=true" >> $GITHUB_ENV
- fi
- if changed 'cmake' || changed 'CMake'; then
- echo "CMAKE_FORMAT=true" >> $GITHUB_ENV
- fi
- if changed '^cpp/src'; then
- echo "CLANG_FORMAT_CPP=true" >> $GITHUB_ENV
- fi
- if changed '^r/src'; then
- echo "CLANG_FORMAT_R=true" >> $GITHUB_ENV
- fi
- - name: Run cmake_format
- if: env.CMAKE_FORMAT == 'true' || endsWith(github.event.comment.body, 'everything')
- run: |
- set -ex
- export PATH=/home/runner/.local/bin:$PATH
- python3 -m pip install --upgrade pip setuptools wheel
- python3 -m pip install -r dev/archery/requirements-lint.txt
- python3 run-cmake-format.py
- - name: Run clang-format on cpp
- if: env.CLANG_FORMAT_CPP == 'true' || endsWith(github.event.comment.body, 'everything')
- run: |
- . .env # To get the clang version we use
- cpp/build-support/run_clang_format.py \
- --clang_format_binary=clang-format-${CLANG_TOOLS} \
- --exclude_glob=cpp/build-support/lint_exclusions.txt \
- --source_dir=cpp/src --quiet --fix
- - name: Run clang-format on r
- if: env.CLANG_FORMAT_R == 'true' || endsWith(github.event.comment.body, 'everything')
- run: |
- . .env # To get the clang version we use
- cpp/build-support/run_clang_format.py \
- --clang_format_binary=clang-format-${CLANG_TOOLS} \
- --exclude_glob=cpp/build-support/lint_exclusions.txt \
- --source_dir=r/src --quiet --fix
- - uses: r-lib/actions/setup-r@v1
- if: env.R_DOCS == 'true' || endsWith(github.event.comment.body, 'everything')
- - name: Update R docs
- if: env.R_DOCS == 'true' || endsWith(github.event.comment.body, 'everything')
- shell: Rscript {0}
- run: |
- source("ci/etc/rprofile")
- install.packages(c("remotes", "roxygen2"))
- remotes::install_deps("r")
- roxygen2::roxygenize("r")
- - name: Commit results
- run: |
- git config user.name "$(git log -1 --pretty=format:%an)"
- git config user.email "$(git log -1 --pretty=format:%ae)"
- git commit -a -m 'Autoformat/render all the things [automated commit]' || echo "No changes to commit"
- - uses: r-lib/actions/pr-push@master
- with:
- repo-token: ${{ secrets.GITHUB_TOKEN }}
-
rebase:
name: "Rebase"
if: startsWith(github.event.comment.body, '@github-actions rebase')
diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml
index 545cb97..548f0dd 100644
--- a/.github/workflows/dev.yml
+++ b/.github/workflows/dev.yml
@@ -27,19 +27,31 @@
ARCHERY_DOCKER_PASSWORD: ${{ secrets.DOCKERHUB_TOKEN }}
jobs:
- lint:
- name: Lint C++, Python, R, Rust, Docker, RAT
+
+ rat:
+ name: Release Audit Tool (RAT)
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - name: Checkout Arrow
+ uses: actions/checkout@v2
+ with:
+ repository: apache/arrow
+ submodules: true
+ fetch-depth: 0
+ - name: Checkout Arrow Rust
+ uses: actions/checkout@v2
+ with:
+ path: rust
+ fetch-depth: 0
- name: Setup Python
uses: actions/setup-python@v1
with:
python-version: 3.8
- name: Setup Archery
- run: pip install -e dev/archery[docker]
+ run: pip install -e dev/archery[lint]
- name: Lint
run: archery lint --rat
+
prettier:
name: Use prettier to check formatting of documents
runs-on: ubuntu-latest
diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml
index 115bfad..cab6dd3 100644
--- a/.github/workflows/integration.yml
+++ b/.github/workflows/integration.yml
@@ -33,16 +33,13 @@
repository: apache/arrow
submodules: true
fetch-depth: 0
- # this is temporary: once rust is removed from `apache/arrow`, we are good to go.
- - name: Remove Rust from arrow
- run: rm -rf rust/
- name: Checkout Arrow Rust
uses: actions/checkout@v2
with:
path: rust
fetch-depth: 0
- name: Setup Python
- uses: actions/setup-python@v1
+ uses: actions/setup-python@v2
with:
python-version: 3.8
- name: Setup Archery
diff --git a/dev/archery/MANIFEST.in b/dev/archery/MANIFEST.in
deleted file mode 100644
index 90fe034..0000000
--- a/dev/archery/MANIFEST.in
+++ /dev/null
@@ -1,4 +0,0 @@
-include ../../LICENSE.txt
-include ../../NOTICE.txt
-
-include archery/reports/*
diff --git a/dev/archery/archery/__init__.py b/dev/archery/archery/__init__.py
deleted file mode 100644
index 13a8339..0000000
--- a/dev/archery/archery/__init__.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
diff --git a/dev/archery/archery/benchmark/__init__.py b/dev/archery/archery/benchmark/__init__.py
deleted file mode 100644
index 13a8339..0000000
--- a/dev/archery/archery/benchmark/__init__.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
diff --git a/dev/archery/archery/benchmark/codec.py b/dev/archery/archery/benchmark/codec.py
deleted file mode 100644
index 4157890..0000000
--- a/dev/archery/archery/benchmark/codec.py
+++ /dev/null
@@ -1,97 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-
-import json
-
-from ..benchmark.core import Benchmark, BenchmarkSuite
-from ..benchmark.runner import BenchmarkRunner, StaticBenchmarkRunner
-from ..benchmark.compare import BenchmarkComparator
-
-
-class JsonEncoder(json.JSONEncoder):
- def default(self, o):
- if isinstance(o, Benchmark):
- return BenchmarkCodec.encode(o)
-
- if isinstance(o, BenchmarkSuite):
- return BenchmarkSuiteCodec.encode(o)
-
- if isinstance(o, BenchmarkRunner):
- return BenchmarkRunnerCodec.encode(o)
-
- if isinstance(o, BenchmarkComparator):
- return BenchmarkComparatorCodec.encode(o)
-
- return json.JSONEncoder.default(self, o)
-
-
-class BenchmarkCodec:
- @staticmethod
- def encode(b):
- return {
- "name": b.name,
- "unit": b.unit,
- "less_is_better": b.less_is_better,
- "values": b.values,
- "time_unit": b.time_unit,
- "times": b.times,
- "counters": b.counters,
- }
-
- @staticmethod
- def decode(dct, **kwargs):
- return Benchmark(**dct, **kwargs)
-
-
-class BenchmarkSuiteCodec:
- @staticmethod
- def encode(bs):
- return {
- "name": bs.name,
- "benchmarks": [BenchmarkCodec.encode(b) for b in bs.benchmarks]
- }
-
- @staticmethod
- def decode(dct, **kwargs):
- benchmarks = [BenchmarkCodec.decode(b)
- for b in dct.pop("benchmarks", [])]
- return BenchmarkSuite(benchmarks=benchmarks, **dct, **kwargs)
-
-
-class BenchmarkRunnerCodec:
- @staticmethod
- def encode(br):
- return {"suites": [BenchmarkSuiteCodec.encode(s) for s in br.suites]}
-
- @staticmethod
- def decode(dct, **kwargs):
- suites = [BenchmarkSuiteCodec.decode(s)
- for s in dct.pop("suites", [])]
- return StaticBenchmarkRunner(suites=suites, **dct, **kwargs)
-
-
-class BenchmarkComparatorCodec:
- @staticmethod
- def encode(bc):
- comparator = bc.formatted
-
- suite_name = bc.suite_name
- if suite_name:
- comparator["suite"] = suite_name
-
- return comparator
diff --git a/dev/archery/archery/benchmark/compare.py b/dev/archery/archery/benchmark/compare.py
deleted file mode 100644
index 622b801..0000000
--- a/dev/archery/archery/benchmark/compare.py
+++ /dev/null
@@ -1,173 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-
-# Define a global regression threshold as 5%. This is purely subjective and
-# flawed. This does not track cumulative regression.
-DEFAULT_THRESHOLD = 0.05
-
-
-def items_per_seconds_fmt(value):
- if value < 1000:
- return "{} items/sec".format(value)
- if value < 1000**2:
- return "{:.3f}K items/sec".format(value / 1000)
- if value < 1000**3:
- return "{:.3f}M items/sec".format(value / 1000**2)
- else:
- return "{:.3f}G items/sec".format(value / 1000**3)
-
-
-def bytes_per_seconds_fmt(value):
- if value < 1024:
- return "{} bytes/sec".format(value)
- if value < 1024**2:
- return "{:.3f} KiB/sec".format(value / 1024)
- if value < 1024**3:
- return "{:.3f} MiB/sec".format(value / 1024**2)
- if value < 1024**4:
- return "{:.3f} GiB/sec".format(value / 1024**3)
- else:
- return "{:.3f} TiB/sec".format(value / 1024**4)
-
-
-def change_fmt(value):
- return "{:.3%}".format(value)
-
-
-def formatter_for_unit(unit):
- if unit == "bytes_per_second":
- return bytes_per_seconds_fmt
- elif unit == "items_per_second":
- return items_per_seconds_fmt
- else:
- return lambda x: x
-
-
-class BenchmarkComparator:
- """ Compares two benchmarks.
-
- Encodes the logic of comparing two benchmarks and taking a decision on
- if it induce a regression.
- """
-
- def __init__(self, contender, baseline, threshold=DEFAULT_THRESHOLD,
- suite_name=None):
- self.contender = contender
- self.baseline = baseline
- self.threshold = threshold
- self.suite_name = suite_name
-
- @property
- def name(self):
- return self.baseline.name
-
- @property
- def less_is_better(self):
- return self.baseline.less_is_better
-
- @property
- def unit(self):
- return self.baseline.unit
-
- @property
- def change(self):
- new = self.contender.value
- old = self.baseline.value
-
- if old == 0 and new == 0:
- return 0.0
- if old == 0:
- return 0.0
-
- return float(new - old) / abs(old)
-
- @property
- def confidence(self):
- """ Indicate if a comparison of benchmarks should be trusted. """
- return True
-
- @property
- def regression(self):
- change = self.change
- adjusted_change = change if self.less_is_better else -change
- return (self.confidence and adjusted_change > self.threshold)
-
- @property
- def formatted(self):
- fmt = formatter_for_unit(self.unit)
- return {
- "benchmark": self.name,
- "change": change_fmt(self.change),
- "regression": self.regression,
- "baseline": fmt(self.baseline.value),
- "contender": fmt(self.contender.value),
- "unit": self.unit,
- "less_is_better": self.less_is_better,
- "counters": str(self.baseline.counters)
- }
-
- def compare(self, comparator=None):
- return {
- "benchmark": self.name,
- "change": self.change,
- "regression": self.regression,
- "baseline": self.baseline.value,
- "contender": self.contender.value,
- "unit": self.unit,
- "less_is_better": self.less_is_better,
- "counters": self.baseline.counters
- }
-
- def __call__(self, **kwargs):
- return self.compare(**kwargs)
-
-
-def pairwise_compare(contender, baseline):
- dict_contender = {e.name: e for e in contender}
- dict_baseline = {e.name: e for e in baseline}
-
- for name in (dict_contender.keys() & dict_baseline.keys()):
- yield name, (dict_contender[name], dict_baseline[name])
-
-
-class RunnerComparator:
- """ Compares suites/benchmarks from runners.
-
- It is up to the caller that ensure that runners are compatible (both from
- the same language implementation).
- """
-
- def __init__(self, contender, baseline, threshold=DEFAULT_THRESHOLD):
- self.contender = contender
- self.baseline = baseline
- self.threshold = threshold
-
- @property
- def comparisons(self):
- contender = self.contender.suites
- baseline = self.baseline.suites
- suites = pairwise_compare(contender, baseline)
-
- for suite_name, (suite_cont, suite_base) in suites:
- benchmarks = pairwise_compare(
- suite_cont.benchmarks, suite_base.benchmarks)
-
- for _, (bench_cont, bench_base) in benchmarks:
- yield BenchmarkComparator(bench_cont, bench_base,
- threshold=self.threshold,
- suite_name=suite_name)
diff --git a/dev/archery/archery/benchmark/core.py b/dev/archery/archery/benchmark/core.py
deleted file mode 100644
index 5a92271..0000000
--- a/dev/archery/archery/benchmark/core.py
+++ /dev/null
@@ -1,57 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-
-def median(values):
- n = len(values)
- if n == 0:
- raise ValueError("median requires at least one value")
- elif n % 2 == 0:
- return (values[(n // 2) - 1] + values[n // 2]) / 2
- else:
- return values[n // 2]
-
-
-class Benchmark:
- def __init__(self, name, unit, less_is_better, values, time_unit,
- times, counters=None):
- self.name = name
- self.unit = unit
- self.less_is_better = less_is_better
- self.values = sorted(values)
- self.time_unit = time_unit
- self.times = sorted(times)
- self.median = median(self.values)
- self.counters = counters or {}
-
- @property
- def value(self):
- return self.median
-
- def __repr__(self):
- return "Benchmark[name={},value={}]".format(self.name, self.value)
-
-
-class BenchmarkSuite:
- def __init__(self, name, benchmarks):
- self.name = name
- self.benchmarks = benchmarks
-
- def __repr__(self):
- return "BenchmarkSuite[name={}, benchmarks={}]".format(
- self.name, self.benchmarks
- )
diff --git a/dev/archery/archery/benchmark/google.py b/dev/archery/archery/benchmark/google.py
deleted file mode 100644
index ebcc526..0000000
--- a/dev/archery/archery/benchmark/google.py
+++ /dev/null
@@ -1,174 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-from itertools import filterfalse, groupby, tee
-import json
-import subprocess
-from tempfile import NamedTemporaryFile
-
-from .core import Benchmark
-from ..utils.command import Command
-
-
-def partition(pred, iterable):
- # adapted from python's examples
- t1, t2 = tee(iterable)
- return list(filter(pred, t1)), list(filterfalse(pred, t2))
-
-
-class GoogleBenchmarkCommand(Command):
- """ Run a google benchmark binary.
-
- This assumes the binary supports the standard command line options,
- notably `--benchmark_filter`, `--benchmark_format`, etc...
- """
-
- def __init__(self, benchmark_bin, benchmark_filter=None):
- self.bin = benchmark_bin
- self.benchmark_filter = benchmark_filter
-
- def list_benchmarks(self):
- argv = ["--benchmark_list_tests"]
- if self.benchmark_filter:
- argv.append("--benchmark_filter={}".format(self.benchmark_filter))
- result = self.run(*argv, stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
- return str.splitlines(result.stdout.decode("utf-8"))
-
- def results(self, repetitions=1):
- with NamedTemporaryFile() as out:
- argv = ["--benchmark_repetitions={}".format(repetitions),
- "--benchmark_out={}".format(out.name),
- "--benchmark_out_format=json"]
-
- if self.benchmark_filter:
- argv.append(
- "--benchmark_filter={}".format(self.benchmark_filter)
- )
-
- self.run(*argv, check=True)
- return json.load(out)
-
-
-class GoogleBenchmarkObservation:
- """ Represents one run of a single (google c++) benchmark.
-
- Aggregates are reported by Google Benchmark executables alongside
- other observations whenever repetitions are specified (with
- `--benchmark_repetitions` on the bare benchmark, or with the
- archery option `--repetitions`). Aggregate observations are not
- included in `GoogleBenchmark.runs`.
-
- RegressionSumKernel/32768/0 1 us 1 us 25.8077GB/s
- RegressionSumKernel/32768/0 1 us 1 us 25.7066GB/s
- RegressionSumKernel/32768/0 1 us 1 us 25.1481GB/s
- RegressionSumKernel/32768/0 1 us 1 us 25.846GB/s
- RegressionSumKernel/32768/0 1 us 1 us 25.6453GB/s
- RegressionSumKernel/32768/0_mean 1 us 1 us 25.6307GB/s
- RegressionSumKernel/32768/0_median 1 us 1 us 25.7066GB/s
- RegressionSumKernel/32768/0_stddev 0 us 0 us 288.046MB/s
- """
-
- def __init__(self, name, real_time, cpu_time, time_unit, run_type,
- size=None, bytes_per_second=None, items_per_second=None,
- **counters):
- self._name = name
- self.real_time = real_time
- self.cpu_time = cpu_time
- self.time_unit = time_unit
- self.run_type = run_type
- self.size = size
- self.bytes_per_second = bytes_per_second
- self.items_per_second = items_per_second
- self.counters = counters
-
- @property
- def is_aggregate(self):
- """ Indicate if the observation is a run or an aggregate. """
- return self.run_type == "aggregate"
-
- @property
- def is_realtime(self):
- """ Indicate if the preferred value is realtime instead of cputime. """
- return self.name.find("/real_time") != -1
-
- @property
- def name(self):
- name = self._name
- return name.rsplit("_", maxsplit=1)[0] if self.is_aggregate else name
-
- @property
- def time(self):
- return self.real_time if self.is_realtime else self.cpu_time
-
- @property
- def value(self):
- """ Return the benchmark value."""
- return self.bytes_per_second or self.items_per_second or self.time
-
- @property
- def unit(self):
- if self.bytes_per_second:
- return "bytes_per_second"
- elif self.items_per_second:
- return "items_per_second"
- else:
- return self.time_unit
-
- def __repr__(self):
- return str(self.value)
-
-
-class GoogleBenchmark(Benchmark):
- """ A set of GoogleBenchmarkObservations. """
-
- def __init__(self, name, runs):
- """ Initialize a GoogleBenchmark.
-
- Parameters
- ----------
- name: str
- Name of the benchmark
- runs: list(GoogleBenchmarkObservation)
- Repetitions of GoogleBenchmarkObservation run.
-
- """
- self.name = name
- # exclude google benchmark aggregate artifacts
- _, runs = partition(lambda b: b.is_aggregate, runs)
- self.runs = sorted(runs, key=lambda b: b.value)
- unit = self.runs[0].unit
- time_unit = self.runs[0].time_unit
- less_is_better = not unit.endswith("per_second")
- values = [b.value for b in self.runs]
- times = [b.real_time for b in self.runs]
- # Slight kludge to extract the UserCounters for each benchmark
- counters = self.runs[0].counters
- super().__init__(name, unit, less_is_better, values, time_unit, times,
- counters)
-
- def __repr__(self):
- return "GoogleBenchmark[name={},runs={}]".format(self.names, self.runs)
-
- @classmethod
- def from_json(cls, payload):
- def group_key(x):
- return x.name
-
- benchmarks = map(lambda x: GoogleBenchmarkObservation(**x), payload)
- groups = groupby(sorted(benchmarks, key=group_key), group_key)
- return [cls(k, list(bs)) for k, bs in groups]
diff --git a/dev/archery/archery/benchmark/runner.py b/dev/archery/archery/benchmark/runner.py
deleted file mode 100644
index 5718bca..0000000
--- a/dev/archery/archery/benchmark/runner.py
+++ /dev/null
@@ -1,212 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-import glob
-import json
-import os
-import re
-
-from .core import BenchmarkSuite
-from .google import GoogleBenchmarkCommand, GoogleBenchmark
-from ..lang.cpp import CppCMakeDefinition, CppConfiguration
-from ..utils.cmake import CMakeBuild
-from ..utils.logger import logger
-
-
-def regex_filter(re_expr):
- if re_expr is None:
- return lambda s: True
- re_comp = re.compile(re_expr)
- return lambda s: re_comp.search(s)
-
-
-DEFAULT_REPETITIONS = 1
-
-
-class BenchmarkRunner:
- def __init__(self, suite_filter=None, benchmark_filter=None,
- repetitions=DEFAULT_REPETITIONS):
- self.suite_filter = suite_filter
- self.benchmark_filter = benchmark_filter
- self.repetitions = repetitions
-
- @property
- def suites(self):
- raise NotImplementedError("BenchmarkRunner must implement suites")
-
- @staticmethod
- def from_rev_or_path(src, root, rev_or_path, cmake_conf, **kwargs):
- """ Returns a BenchmarkRunner from a path or a git revision.
-
- First, it checks if `rev_or_path` is a valid path (or string) of a json
- object that can deserialize to a BenchmarkRunner. If so, it initialize
- a StaticBenchmarkRunner from it. This allows memoizing the result of a
- run in a file or a string.
-
- Second, it checks if `rev_or_path` points to a valid CMake build
- directory. If so, it creates a CppBenchmarkRunner with this existing
- CMakeBuild.
-
- Otherwise, it assumes `rev_or_path` is a revision and clone/checkout
- the given revision and create a fresh CMakeBuild.
- """
- build = None
- if StaticBenchmarkRunner.is_json_result(rev_or_path):
- return StaticBenchmarkRunner.from_json(rev_or_path, **kwargs)
- elif CMakeBuild.is_build_dir(rev_or_path):
- build = CMakeBuild.from_path(rev_or_path)
- return CppBenchmarkRunner(build, **kwargs)
- else:
- # Revisions can references remote via the `/` character, ensure
- # that the revision is path friendly
- path_rev = rev_or_path.replace("/", "_")
- root_rev = os.path.join(root, path_rev)
- os.mkdir(root_rev)
-
- clone_dir = os.path.join(root_rev, "arrow")
- # Possibly checkout the sources at given revision, no need to
- # perform cleanup on cloned repository as root_rev is reclaimed.
- src_rev, _ = src.at_revision(rev_or_path, clone_dir)
- cmake_def = CppCMakeDefinition(src_rev.cpp, cmake_conf)
- build_dir = os.path.join(root_rev, "build")
- return CppBenchmarkRunner(cmake_def.build(build_dir), **kwargs)
-
-
-class StaticBenchmarkRunner(BenchmarkRunner):
- """ Run suites from a (static) set of suites. """
-
- def __init__(self, suites, **kwargs):
- self._suites = suites
- super().__init__(**kwargs)
-
- @property
- def list_benchmarks(self):
- for suite in self._suites:
- for benchmark in suite.benchmarks:
- yield "{}.{}".format(suite.name, benchmark.name)
-
- @property
- def suites(self):
- suite_fn = regex_filter(self.suite_filter)
- benchmark_fn = regex_filter(self.benchmark_filter)
-
- for suite in (s for s in self._suites if suite_fn(s.name)):
- benchmarks = [b for b in suite.benchmarks if benchmark_fn(b.name)]
- yield BenchmarkSuite(suite.name, benchmarks)
-
- @classmethod
- def is_json_result(cls, path_or_str):
- builder = None
- try:
- builder = cls.from_json(path_or_str)
- except BaseException:
- pass
-
- return builder is not None
-
- @staticmethod
- def from_json(path_or_str, **kwargs):
- # .codec imported here to break recursive imports
- from .codec import BenchmarkRunnerCodec
- if os.path.isfile(path_or_str):
- with open(path_or_str) as f:
- loaded = json.load(f)
- else:
- loaded = json.loads(path_or_str)
- return BenchmarkRunnerCodec.decode(loaded, **kwargs)
-
- def __repr__(self):
- return "BenchmarkRunner[suites={}]".format(list(self.suites))
-
-
-class CppBenchmarkRunner(BenchmarkRunner):
- """ Run suites from a CMakeBuild. """
-
- def __init__(self, build, **kwargs):
- """ Initialize a CppBenchmarkRunner. """
- self.build = build
- super().__init__(**kwargs)
-
- @staticmethod
- def default_configuration(**kwargs):
- """ Returns the default benchmark configuration. """
- return CppConfiguration(
- build_type="release", with_tests=False, with_benchmarks=True,
- with_compute=True,
- with_csv=True,
- with_dataset=True,
- with_json=True,
- with_parquet=True,
- with_python=False,
- with_brotli=True,
- with_bz2=True,
- with_lz4=True,
- with_snappy=True,
- with_zlib=True,
- with_zstd=True,
- **kwargs)
-
- @property
- def suites_binaries(self):
- """ Returns a list of benchmark binaries for this build. """
- # Ensure build is up-to-date to run benchmarks
- self.build()
- # Not the best method, but works for now
- glob_expr = os.path.join(self.build.binaries_dir, "*-benchmark")
- return {os.path.basename(b): b for b in glob.glob(glob_expr)}
-
- def suite(self, name, suite_bin):
- """ Returns the resulting benchmarks for a given suite. """
- suite_cmd = GoogleBenchmarkCommand(suite_bin, self.benchmark_filter)
-
- # Ensure there will be data
- benchmark_names = suite_cmd.list_benchmarks()
- if not benchmark_names:
- return None
-
- results = suite_cmd.results(repetitions=self.repetitions)
- benchmarks = GoogleBenchmark.from_json(results.get("benchmarks"))
- return BenchmarkSuite(name, benchmarks)
-
- @property
- def list_benchmarks(self):
- for suite_name, suite_bin in self.suites_binaries.items():
- suite_cmd = GoogleBenchmarkCommand(suite_bin)
- for benchmark_name in suite_cmd.list_benchmarks():
- yield "{}.{}".format(suite_name, benchmark_name)
-
- @property
- def suites(self):
- """ Returns all suite for a runner. """
- suite_matcher = regex_filter(self.suite_filter)
-
- suite_and_binaries = self.suites_binaries
- for suite_name in suite_and_binaries:
- if not suite_matcher(suite_name):
- logger.debug("Ignoring suite {}".format(suite_name))
- continue
-
- suite_bin = suite_and_binaries[suite_name]
- suite = self.suite(suite_name, suite_bin)
-
- # Filter may exclude all benchmarks
- if not suite:
- logger.debug("Suite {} executed but no results"
- .format(suite_name))
- continue
-
- yield suite
diff --git a/dev/archery/archery/bot.py b/dev/archery/archery/bot.py
deleted file mode 100644
index c69cf91..0000000
--- a/dev/archery/archery/bot.py
+++ /dev/null
@@ -1,261 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-import os
-import shlex
-from pathlib import Path
-from functools import partial
-import tempfile
-
-import click
-import github
-
-from .utils.git import git
-from .utils.logger import logger
-from .crossbow import Repo, Queue, Config, Target, Job, CommentReport
-
-
-class EventError(Exception):
- pass
-
-
-class CommandError(Exception):
-
- def __init__(self, message):
- self.message = message
-
-
-class _CommandMixin:
-
- def get_help_option(self, ctx):
- def show_help(ctx, param, value):
- if value and not ctx.resilient_parsing:
- raise click.UsageError(ctx.get_help())
- option = super().get_help_option(ctx)
- option.callback = show_help
- return option
-
- def __call__(self, message, **kwargs):
- args = shlex.split(message)
- try:
- with self.make_context(self.name, args=args, obj=kwargs) as ctx:
- return self.invoke(ctx)
- except click.ClickException as e:
- raise CommandError(e.format_message())
-
-
-class Command(_CommandMixin, click.Command):
- pass
-
-
-class Group(_CommandMixin, click.Group):
-
- def command(self, *args, **kwargs):
- kwargs.setdefault('cls', Command)
- return super().command(*args, **kwargs)
-
- def group(self, *args, **kwargs):
- kwargs.setdefault('cls', Group)
- return super().group(*args, **kwargs)
-
- def parse_args(self, ctx, args):
- if not args and self.no_args_is_help and not ctx.resilient_parsing:
- raise click.UsageError(ctx.get_help())
- return super().parse_args(ctx, args)
-
-
-command = partial(click.command, cls=Command)
-group = partial(click.group, cls=Group)
-
-
-class CommentBot:
-
- def __init__(self, name, handler, token=None):
- # TODO(kszucs): validate
- assert isinstance(name, str)
- assert callable(handler)
- self.name = name
- self.handler = handler
- self.github = github.Github(token)
-
- def parse_command(self, payload):
- # only allow users of apache org to submit commands, for more see
- # https://developer.github.com/v4/enum/commentauthorassociation/
- allowed_roles = {'OWNER', 'MEMBER', 'CONTRIBUTOR'}
- mention = '@{}'.format(self.name)
- comment = payload['comment']
-
- if payload['sender']['login'] == self.name:
- raise EventError("Don't respond to itself")
- elif payload['action'] not in {'created', 'edited'}:
- raise EventError("Don't respond to comment deletion")
- elif comment['author_association'] not in allowed_roles:
- raise EventError(
- "Don't respond to comments from non-authorized users"
- )
- elif not comment['body'].lstrip().startswith(mention):
- raise EventError("The bot is not mentioned")
-
- return payload['comment']['body'].split(mention)[-1].strip()
-
- def handle(self, event, payload):
- try:
- command = self.parse_command(payload)
- except EventError as e:
- logger.error(e)
- # see the possible reasons in the validate method
- return
-
- if event == 'issue_comment':
- return self.handle_issue_comment(command, payload)
- elif event == 'pull_request_review_comment':
- return self.handle_review_comment(command, payload)
- else:
- raise ValueError("Unexpected event type {}".format(event))
-
- def handle_issue_comment(self, command, payload):
- repo = self.github.get_repo(payload['repository']['id'], lazy=True)
- issue = repo.get_issue(payload['issue']['number'])
-
- try:
- pull = issue.as_pull_request()
- except github.GithubException:
- return issue.create_comment(
- "The comment bot only listens to pull request comments!"
- )
-
- comment = pull.get_issue_comment(payload['comment']['id'])
- try:
- self.handler(command, issue=issue, pull_request=pull,
- comment=comment)
- except CommandError as e:
- logger.error(e)
- pull.create_issue_comment("```\n{}\n```".format(e.message))
- except Exception as e:
- logger.exception(e)
- comment.create_reaction('-1')
- else:
- comment.create_reaction('+1')
-
- def handle_review_comment(self, payload):
- raise NotImplementedError()
-
-
-@group(name='@github-actions')
-@click.pass_context
-def actions(ctx):
- """Ursabot"""
- ctx.ensure_object(dict)
-
-
-@actions.group()
-@click.option('--crossbow', '-c', default='ursacomputing/crossbow',
- help='Crossbow repository on github to use')
-@click.pass_obj
-def crossbow(obj, crossbow):
- """
- Trigger crossbow builds for this pull request
- """
- obj['crossbow_repo'] = crossbow
-
-
-def _clone_arrow_and_crossbow(dest, crossbow_repo, pull_request):
- """
- Clone the repositories and initialize crossbow objects.
-
- Parameters
- ----------
- dest : Path
- Filesystem path to clone the repositories to.
- crossbow_repo : str
- Github repository name, like kszucs/crossbow.
- pull_request : pygithub.PullRequest
- Object containing information about the pull request the comment bot
- was triggered from.
- """
- arrow_path = dest / 'arrow'
- queue_path = dest / 'crossbow'
-
- # clone arrow and checkout the pull request's branch
- pull_request_ref = 'pull/{}/head:{}'.format(
- pull_request.number, pull_request.head.ref
- )
- git.clone(pull_request.base.repo.clone_url, str(arrow_path))
- git.fetch('origin', pull_request_ref, git_dir=arrow_path)
- git.checkout(pull_request.head.ref, git_dir=arrow_path)
-
- # clone crossbow repository
- crossbow_url = 'https://github.com/{}'.format(crossbow_repo)
- git.clone(crossbow_url, str(queue_path))
-
- # initialize crossbow objects
- github_token = os.environ['CROSSBOW_GITHUB_TOKEN']
- arrow = Repo(arrow_path)
- queue = Queue(queue_path, github_token=github_token, require_https=True)
-
- return (arrow, queue)
-
-
-@crossbow.command()
-@click.argument('tasks', nargs=-1, required=False)
-@click.option('--group', '-g', 'groups', multiple=True,
- help='Submit task groups as defined in tests.yml')
-@click.option('--param', '-p', 'params', multiple=True,
- help='Additional task parameters for rendering the CI templates')
-@click.option('--arrow-version', '-v', default=None,
- help='Set target version explicitly.')
-@click.pass_obj
-def submit(obj, tasks, groups, params, arrow_version):
- """
- Submit crossbow testing tasks.
-
- See groups defined in arrow/dev/tasks/tests.yml
- """
- crossbow_repo = obj['crossbow_repo']
- pull_request = obj['pull_request']
- with tempfile.TemporaryDirectory() as tmpdir:
- tmpdir = Path(tmpdir)
- arrow, queue = _clone_arrow_and_crossbow(
- dest=Path(tmpdir),
- crossbow_repo=crossbow_repo,
- pull_request=pull_request,
- )
- # load available tasks configuration and groups from yaml
- config = Config.load_yaml(arrow.path / "dev" / "tasks" / "tasks.yml")
- config.validate()
-
- # initialize the crossbow build's target repository
- target = Target.from_repo(arrow, version=arrow_version,
- remote=pull_request.head.repo.clone_url,
- branch=pull_request.head.ref)
-
- # parse additional job parameters
- params = dict([p.split("=") for p in params])
-
- # instantiate the job object
- job = Job.from_config(config=config, target=target, tasks=tasks,
- groups=groups, params=params)
-
- # add the job to the crossbow queue and push to the remote repository
- queue.put(job, prefix="actions")
- queue.push()
-
- # render the response comment's content
- report = CommentReport(job, crossbow_repo=crossbow_repo)
-
- # send the response
- pull_request.create_issue_comment(report.show())
diff --git a/dev/archery/archery/cli.py b/dev/archery/archery/cli.py
deleted file mode 100644
index 4bbde75..0000000
--- a/dev/archery/archery/cli.py
+++ /dev/null
@@ -1,1092 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-from collections import namedtuple
-from io import StringIO
-import click
-import errno
-import json
-import logging
-import os
-import pathlib
-import sys
-
-from .benchmark.codec import JsonEncoder
-from .benchmark.compare import RunnerComparator, DEFAULT_THRESHOLD
-from .benchmark.runner import BenchmarkRunner, CppBenchmarkRunner
-from .lang.cpp import CppCMakeDefinition, CppConfiguration
-from .utils.lint import linter, python_numpydoc, LintValidationException
-from .utils.logger import logger, ctx as log_ctx
-from .utils.source import ArrowSources, InvalidArrowSource
-from .utils.tmpdir import tmpdir
-
-# Set default logging to INFO in command line.
-logging.basicConfig(level=logging.INFO)
-
-
-class ArrowBool(click.types.BoolParamType):
- """
- ArrowBool supports the 'ON' and 'OFF' values on top of the values
- supported by BoolParamType. This is convenient to port script which exports
- CMake options variables.
- """
- name = "boolean"
-
- def convert(self, value, param, ctx):
- if isinstance(value, str):
- lowered = value.lower()
- if lowered == "on":
- return True
- elif lowered == "off":
- return False
-
- return super().convert(value, param, ctx)
-
-
-BOOL = ArrowBool()
-
-
-@click.group()
-@click.option("--debug", type=BOOL, is_flag=True, default=False,
- help="Increase logging with debugging output.")
-@click.option("--pdb", type=BOOL, is_flag=True, default=False,
- help="Invoke pdb on uncaught exception.")
-@click.option("-q", "--quiet", type=BOOL, is_flag=True, default=False,
- help="Silence executed commands.")
-@click.pass_context
-def archery(ctx, debug, pdb, quiet):
- """ Apache Arrow developer utilities.
-
- See sub-commands help with `archery <cmd> --help`.
-
- """
- # Ensure ctx.obj exists
- ctx.ensure_object(dict)
-
- log_ctx.quiet = quiet
- if debug:
- logger.setLevel(logging.DEBUG)
-
- ctx.debug = debug
-
- if pdb:
- import pdb
- sys.excepthook = lambda t, v, e: pdb.pm()
-
-
-def validate_arrow_sources(ctx, param, src):
- """ Ensure a directory contains Arrow cpp sources. """
- try:
- return ArrowSources.find(src)
- except InvalidArrowSource as e:
- raise click.BadParameter(str(e))
-
-
-build_dir_type = click.Path(dir_okay=True, file_okay=False, resolve_path=True)
-# Supported build types
-build_type = click.Choice(["debug", "relwithdebinfo", "release"],
- case_sensitive=False)
-# Supported warn levels
-warn_level_type = click.Choice(["everything", "checkin", "production"],
- case_sensitive=False)
-
-simd_level = click.Choice(["NONE", "SSE4_2", "AVX2", "AVX512"],
- case_sensitive=True)
-
-
-def cpp_toolchain_options(cmd):
- options = [
- click.option("--cc", metavar="<compiler>", help="C compiler."),
- click.option("--cxx", metavar="<compiler>", help="C++ compiler."),
- click.option("--cxx-flags", help="C++ compiler flags."),
- click.option("--cpp-package-prefix",
- help=("Value to pass for ARROW_PACKAGE_PREFIX and "
- "use ARROW_DEPENDENCY_SOURCE=SYSTEM"))
- ]
- return _apply_options(cmd, options)
-
-
-def _apply_options(cmd, options):
- for option in options:
- cmd = option(cmd)
- return cmd
-
-
-@archery.command(short_help="Initialize an Arrow C++ build")
-@click.option("--src", metavar="<arrow_src>", default=None,
- callback=validate_arrow_sources,
- help="Specify Arrow source directory")
-# toolchain
-@cpp_toolchain_options
-@click.option("--build-type", default=None, type=build_type,
- help="CMake's CMAKE_BUILD_TYPE")
-@click.option("--warn-level", default="production", type=warn_level_type,
- help="Controls compiler warnings -W(no-)error.")
-@click.option("--use-gold-linker", default=True, type=BOOL,
- help="Toggles ARROW_USE_LD_GOLD option.")
-@click.option("--simd-level", default="SSE4_2", type=simd_level,
- help="Toggles ARROW_SIMD_LEVEL option.")
-# Tests and benchmarks
-@click.option("--with-tests", default=True, type=BOOL,
- help="Build with tests.")
-@click.option("--with-benchmarks", default=None, type=BOOL,
- help="Build with benchmarks.")
-@click.option("--with-examples", default=None, type=BOOL,
- help="Build with examples.")
-@click.option("--with-integration", default=None, type=BOOL,
- help="Build with integration test executables.")
-# Static checks
-@click.option("--use-asan", default=None, type=BOOL,
- help="Toggle ARROW_USE_ASAN sanitizer.")
-@click.option("--use-tsan", default=None, type=BOOL,
- help="Toggle ARROW_USE_TSAN sanitizer.")
-@click.option("--use-ubsan", default=None, type=BOOL,
- help="Toggle ARROW_USE_UBSAN sanitizer.")
-@click.option("--with-fuzzing", default=None, type=BOOL,
- help="Toggle ARROW_FUZZING.")
-# Components
-@click.option("--with-compute", default=None, type=BOOL,
- help="Build the Arrow compute module.")
-@click.option("--with-csv", default=None, type=BOOL,
- help="Build the Arrow CSV parser module.")
-@click.option("--with-cuda", default=None, type=BOOL,
- help="Build the Arrow CUDA extensions.")
-@click.option("--with-dataset", default=None, type=BOOL,
- help="Build the Arrow dataset module.")
-@click.option("--with-filesystem", default=None, type=BOOL,
- help="Build the Arrow filesystem layer.")
-@click.option("--with-flight", default=None, type=BOOL,
- help="Build with Flight rpc support.")
-@click.option("--with-gandiva", default=None, type=BOOL,
- help="Build with Gandiva expression compiler support.")
-@click.option("--with-hdfs", default=None, type=BOOL,
- help="Build the Arrow HDFS bridge.")
-@click.option("--with-hiveserver2", default=None, type=BOOL,
- help="Build the HiveServer2 client and arrow adapater.")
-@click.option("--with-ipc", default=None, type=BOOL,
- help="Build the Arrow IPC extensions.")
-@click.option("--with-json", default=None, type=BOOL,
- help="Build the Arrow JSON parser module.")
-@click.option("--with-jni", default=None, type=BOOL,
- help="Build the Arrow JNI lib.")
-@click.option("--with-mimalloc", default=None, type=BOOL,
- help="Build the Arrow mimalloc based allocator.")
-@click.option("--with-parquet", default=None, type=BOOL,
- help="Build with Parquet file support.")
-@click.option("--with-plasma", default=None, type=BOOL,
- help="Build with Plasma object store support.")
-@click.option("--with-python", default=None, type=BOOL,
- help="Build the Arrow CPython extesions.")
-@click.option("--with-r", default=None, type=BOOL,
- help="Build the Arrow R extensions. This is not a CMake option, "
- "it will toggle required options")
-@click.option("--with-s3", default=None, type=BOOL,
- help="Build Arrow with S3 support.")
-# Compressions
-@click.option("--with-brotli", default=None, type=BOOL,
- help="Build Arrow with brotli compression.")
-@click.option("--with-bz2", default=None, type=BOOL,
- help="Build Arrow with bz2 compression.")
-@click.option("--with-lz4", default=None, type=BOOL,
- help="Build Arrow with lz4 compression.")
-@click.option("--with-snappy", default=None, type=BOOL,
- help="Build Arrow with snappy compression.")
-@click.option("--with-zlib", default=None, type=BOOL,
- help="Build Arrow with zlib compression.")
-@click.option("--with-zstd", default=None, type=BOOL,
- help="Build Arrow with zstd compression.")
-# CMake extra feature
-@click.option("--cmake-extras", type=str, multiple=True,
- help="Extra flags/options to pass to cmake invocation. "
- "Can be stacked")
-@click.option("--install-prefix", type=str,
- help="Destination directory where files are installed. Expand to"
- "CMAKE_INSTALL_PREFIX. Defaults to to $CONDA_PREFIX if the"
- "variable exists.")
-# misc
-@click.option("-f", "--force", type=BOOL, is_flag=True, default=False,
- help="Delete existing build directory if found.")
-@click.option("--targets", type=str, multiple=True,
- help="Generator targets to run. Can be stacked.")
-@click.argument("build_dir", type=build_dir_type)
-@click.pass_context
-def build(ctx, src, build_dir, force, targets, **kwargs):
- """ Initialize a C++ build directory.
-
- The build command creates a directory initialized with Arrow's cpp source
- cmake and configuration. It can also optionally invoke the generator to
- test the build (and used in scripts).
-
- Note that archery will carry the caller environment. It will also not touch
- an existing directory, one must use the `--force` option to remove the
- existing directory.
-
- Examples:
-
- \b
- # Initialize build with clang8 and avx2 support in directory `clang8-build`
- \b
- archery build --cc=clang-8 --cxx=clang++-8 --cxx-flags=-mavx2 clang8-build
-
- \b
- # Builds and run test
- archery build --targets=all --targets=test build
- """
- # Arrow's cpp cmake configuration
- conf = CppConfiguration(**kwargs)
- # This is a closure around cmake invocation, e.g. calling `def.build()`
- # yields a directory ready to be run with the generator
- cmake_def = CppCMakeDefinition(src.cpp, conf)
- # Create build directory
- build = cmake_def.build(build_dir, force=force)
-
- for target in targets:
- build.run(target)
-
-
-LintCheck = namedtuple('LintCheck', ('option_name', 'help'))
-
-lint_checks = [
- LintCheck('clang-format', "Format C++ files with clang-format."),
- LintCheck('clang-tidy', "Lint C++ files with clang-tidy."),
- LintCheck('cpplint', "Lint C++ files with cpplint."),
- LintCheck('iwyu', "Lint changed C++ files with Include-What-You-Use."),
- LintCheck('python',
- "Format and lint Python files with autopep8 and flake8."),
- LintCheck('numpydoc', "Lint Python files with numpydoc."),
- LintCheck('cmake-format', "Format CMake files with cmake-format.py."),
- LintCheck('rat',
- "Check all sources files for license texts via Apache RAT."),
- LintCheck('r', "Lint R files."),
- LintCheck('rust', "Lint Rust files."),
- LintCheck('docker', "Lint Dockerfiles with hadolint."),
-]
-
-
-def decorate_lint_command(cmd):
- """
- Decorate the lint() command function to add individual per-check options.
- """
- for check in lint_checks:
- option = click.option("--{0}/--no-{0}".format(check.option_name),
- default=None, help=check.help)
- cmd = option(cmd)
- return cmd
-
-
-@archery.command(short_help="Check Arrow source tree for errors")
-@click.option("--src", metavar="<arrow_src>", default=".",
- help="Specify Arrow source directory")
-@click.option("--fix", is_flag=True, type=BOOL, default=False,
- help="Toggle fixing the lint errors if the linter supports it.")
-@click.option("--iwyu_all", is_flag=True, type=BOOL, default=False,
- help="Run IWYU on all C++ files if enabled")
-@click.option("-a", "--all", is_flag=True, default=False,
- help="Enable all checks.")
-@decorate_lint_command
-@click.pass_context
-def lint(ctx, src, fix, iwyu_all, **checks):
- src = ArrowSources(src)
-
- if checks.pop('all'):
- # "--all" is given => enable all non-selected checks
- for k, v in checks.items():
- if v is None:
- checks[k] = True
- if not any(checks.values()):
- raise click.UsageError(
- "Need to enable at least one lint check (try --help)")
- try:
- linter(src, fix, iwyu_all=iwyu_all, **checks)
- except LintValidationException:
- sys.exit(1)
-
-
-@archery.command(short_help="Lint python docstring with NumpyDoc")
-@click.argument('symbols', nargs=-1)
-@click.option("--src", metavar="<arrow_src>", default=None,
- callback=validate_arrow_sources,
- help="Specify Arrow source directory")
-@click.option("--allow-rule", "-a", multiple=True,
- help="Allow only these rules")
-@click.option("--disallow-rule", "-d", multiple=True,
- help="Disallow these rules")
-def numpydoc(src, symbols, allow_rule, disallow_rule):
- """
- Pass list of modules or symbols as arguments to restrict the validation.
-
- By default all modules of pyarrow are tried to be validated.
-
- Examples
- --------
- archery numpydoc pyarrow.dataset
- archery numpydoc pyarrow.csv pyarrow.json pyarrow.parquet
- archery numpydoc pyarrow.array
- """
- disallow_rule = disallow_rule or {'GL01', 'SA01', 'EX01', 'ES01'}
- try:
- results = python_numpydoc(symbols, allow_rules=allow_rule,
- disallow_rule=disallow_rule)
- for result in results:
- result.ok()
- except LintValidationException:
- sys.exit(1)
-
-
-@archery.group()
-@click.pass_context
-def benchmark(ctx):
- """ Arrow benchmarking.
-
- Use the diff sub-command to benchmark revisions, and/or build directories.
- """
- pass
-
-
-def benchmark_common_options(cmd):
- options = [
- click.option("--src", metavar="<arrow_src>", show_default=True,
- default=None, callback=validate_arrow_sources,
- help="Specify Arrow source directory"),
- click.option("--preserve", type=BOOL, default=False, show_default=True,
- is_flag=True,
- help="Preserve workspace for investigation."),
- click.option("--output", metavar="<output>",
- type=click.File("w", encoding="utf8"), default="-",
- help="Capture output result into file."),
- click.option("--cmake-extras", type=str, multiple=True,
- help="Extra flags/options to pass to cmake invocation. "
- "Can be stacked"),
- ]
-
- cmd = cpp_toolchain_options(cmd)
- return _apply_options(cmd, options)
-
-
-def benchmark_filter_options(cmd):
- options = [
- click.option("--suite-filter", metavar="<regex>", show_default=True,
- type=str, default=None,
- help="Regex filtering benchmark suites."),
- click.option("--benchmark-filter", metavar="<regex>",
- show_default=True, type=str, default=None,
- help="Regex filtering benchmarks.")
- ]
- return _apply_options(cmd, options)
-
-
-@benchmark.command(name="list", short_help="List benchmark suite")
-@click.argument("rev_or_path", metavar="[<rev_or_path>]",
- default="WORKSPACE", required=False)
-@benchmark_common_options
-@click.pass_context
-def benchmark_list(ctx, rev_or_path, src, preserve, output, cmake_extras,
- **kwargs):
- """ List benchmark suite.
- """
- with tmpdir(preserve=preserve) as root:
- logger.debug("Running benchmark {}".format(rev_or_path))
-
- conf = CppBenchmarkRunner.default_configuration(
- cmake_extras=cmake_extras, **kwargs)
-
- runner_base = BenchmarkRunner.from_rev_or_path(
- src, root, rev_or_path, conf)
-
- for b in runner_base.list_benchmarks:
- click.echo(b, file=output)
-
-
-@benchmark.command(name="run", short_help="Run benchmark suite")
-@click.argument("rev_or_path", metavar="[<rev_or_path>]",
- default="WORKSPACE", required=False)
-@benchmark_common_options
-@benchmark_filter_options
-@click.option("--repetitions", type=int, default=1, show_default=True,
- help=("Number of repetitions of each benchmark. Increasing "
- "may improve result precision."))
-@click.pass_context
-def benchmark_run(ctx, rev_or_path, src, preserve, output, cmake_extras,
- suite_filter, benchmark_filter, repetitions, **kwargs):
- """ Run benchmark suite.
-
- This command will run the benchmark suite for a single build. This is
- used to capture (and/or publish) the results.
-
- The caller can optionally specify a target which is either a git revision
- (commit, tag, special values like HEAD) or a cmake build directory.
-
- When a commit is referenced, a local clone of the arrow sources (specified
- via --src) is performed and the proper branch is created. This is done in
- a temporary directory which can be left intact with the `--preserve` flag.
-
- The special token "WORKSPACE" is reserved to specify the current git
- workspace. This imply that no clone will be performed.
-
- Examples:
-
- \b
- # Run the benchmarks on current git workspace
- \b
- archery benchmark run
-
- \b
- # Run the benchmarks on current previous commit
- \b
- archery benchmark run HEAD~1
-
- \b
- # Run the benchmarks on current previous commit
- \b
- archery benchmark run --output=run.json
- """
- with tmpdir(preserve=preserve) as root:
- logger.debug("Running benchmark {}".format(rev_or_path))
-
- conf = CppBenchmarkRunner.default_configuration(
- cmake_extras=cmake_extras, **kwargs)
-
- runner_base = BenchmarkRunner.from_rev_or_path(
- src, root, rev_or_path, conf,
- repetitions=repetitions,
- suite_filter=suite_filter, benchmark_filter=benchmark_filter)
-
- json.dump(runner_base, output, cls=JsonEncoder)
-
-
-@benchmark.command(name="diff", short_help="Compare benchmark suites")
-@benchmark_common_options
-@benchmark_filter_options
-@click.option("--threshold", type=float, default=DEFAULT_THRESHOLD,
- show_default=True,
- help="Regression failure threshold in percentage.")
-@click.option("--repetitions", type=int, default=1, show_default=True,
- help=("Number of repetitions of each benchmark. Increasing "
- "may improve result precision."))
-@click.option("--no-counters", type=BOOL, default=False, is_flag=True,
- help="Hide counters field in diff report.")
-@click.argument("contender", metavar="[<contender>",
- default=ArrowSources.WORKSPACE, required=False)
-@click.argument("baseline", metavar="[<baseline>]]", default="origin/master",
- required=False)
-@click.pass_context
-def benchmark_diff(ctx, src, preserve, output, cmake_extras,
- suite_filter, benchmark_filter, repetitions, no_counters,
- threshold, contender, baseline, **kwargs):
- """Compare (diff) benchmark runs.
-
- This command acts like git-diff but for benchmark results.
-
- The caller can optionally specify both the contender and the baseline. If
- unspecified, the contender will default to the current workspace (like git)
- and the baseline will default to master.
-
- Each target (contender or baseline) can either be a git revision
- (commit, tag, special values like HEAD) or a cmake build directory. This
- allow comparing git commits, and/or different compilers and/or compiler
- flags.
-
- When a commit is referenced, a local clone of the arrow sources (specified
- via --src) is performed and the proper branch is created. This is done in
- a temporary directory which can be left intact with the `--preserve` flag.
-
- The special token "WORKSPACE" is reserved to specify the current git
- workspace. This imply that no clone will be performed.
-
- Examples:
-
- \b
- # Compare workspace (contender) with master (baseline)
- \b
- archery benchmark diff
-
- \b
- # Compare master (contender) with latest version (baseline)
- \b
- export LAST=$(git tag -l "apache-arrow-[0-9]*" | sort -rV | head -1)
- \b
- archery benchmark diff master "$LAST"
-
- \b
- # Compare g++7 (contender) with clang++-8 (baseline) builds
- \b
- archery build --with-benchmarks=true \\
- --cxx-flags=-ftree-vectorize \\
- --cc=gcc-7 --cxx=g++-7 gcc7-build
- \b
- archery build --with-benchmarks=true \\
- --cxx-flags=-flax-vector-conversions \\
- --cc=clang-8 --cxx=clang++-8 clang8-build
- \b
- archery benchmark diff gcc7-build clang8-build
-
- \b
- # Compare default targets but scoped to the suites matching
- # `^arrow-compute-aggregate` and benchmarks matching `(Sum|Mean)Kernel`.
- \b
- archery benchmark diff --suite-filter="^arrow-compute-aggregate" \\
- --benchmark-filter="(Sum|Mean)Kernel"
-
- \b
- # Capture result in file `result.json`
- \b
- archery benchmark diff --output=result.json
- \b
- # Equivalently with no stdout clutter.
- archery --quiet benchmark diff > result.json
-
- \b
- # Comparing with a cached results from `archery benchmark run`
- \b
- archery benchmark run --output=run.json HEAD~1
- \b
- # This should not recompute the benchmark from run.json
- archery --quiet benchmark diff WORKSPACE run.json > result.json
- """
- with tmpdir(preserve=preserve) as root:
- logger.debug("Comparing {} (contender) with {} (baseline)"
- .format(contender, baseline))
-
- conf = CppBenchmarkRunner.default_configuration(
- cmake_extras=cmake_extras, **kwargs)
-
- runner_cont = BenchmarkRunner.from_rev_or_path(
- src, root, contender, conf,
- repetitions=repetitions,
- suite_filter=suite_filter,
- benchmark_filter=benchmark_filter)
- runner_base = BenchmarkRunner.from_rev_or_path(
- src, root, baseline, conf,
- repetitions=repetitions,
- suite_filter=suite_filter,
- benchmark_filter=benchmark_filter)
-
- runner_comp = RunnerComparator(runner_cont, runner_base, threshold)
-
- # TODO(kszucs): test that the output is properly formatted jsonlines
- comparisons_json = _get_comparisons_as_json(runner_comp.comparisons)
- formatted = _format_comparisons_with_pandas(comparisons_json,
- no_counters)
- output.write(formatted)
- output.write('\n')
-
-
-def _get_comparisons_as_json(comparisons):
- buf = StringIO()
- for comparator in comparisons:
- json.dump(comparator, buf, cls=JsonEncoder)
- buf.write("\n")
-
- return buf.getvalue()
-
-
-def _format_comparisons_with_pandas(comparisons_json, no_counters):
- import pandas as pd
- df = pd.read_json(StringIO(comparisons_json), lines=True)
- # parse change % so we can sort by it
- df['change %'] = df.pop('change').str[:-1].map(float)
- first_regression = len(df) - df['regression'].sum()
-
- fields = ['benchmark', 'baseline', 'contender', 'change %']
- if not no_counters:
- fields += ['counters']
-
- df = df[fields].sort_values(by='change %', ascending=False)
-
- def labelled(title, df):
- if len(df) == 0:
- return ''
- title += ': ({})'.format(len(df))
- df_str = df.to_string(index=False)
- bar = '-' * df_str.index('\n')
- return '\n'.join([bar, title, bar, df_str])
-
- return '\n\n'.join([labelled('Non-regressions', df[:first_regression]),
- labelled('Regressions', df[first_regression:])])
-
-
-# ----------------------------------------------------------------------
-# Integration testing
-
-def _set_default(opt, default):
- if opt is None:
- return default
- return opt
-
-
-@archery.command(short_help="Execute protocol and Flight integration tests")
-@click.option('--with-all', is_flag=True, default=False,
- help=('Include all known languages by default '
- 'in integration tests'))
-@click.option('--random-seed', type=int, default=12345,
- help="Seed for PRNG when generating test data")
-@click.option('--with-cpp', type=bool, default=False,
- help='Include C++ in integration tests')
-@click.option('--with-java', type=bool, default=False,
- help='Include Java in integration tests')
-@click.option('--with-js', type=bool, default=False,
- help='Include JavaScript in integration tests')
-@click.option('--with-go', type=bool, default=False,
- help='Include Go in integration tests')
-@click.option('--with-rust', type=bool, default=False,
- help='Include Rust in integration tests')
-@click.option('--write_generated_json', default=False,
- help='Generate test JSON to indicated path')
-@click.option('--run-flight', is_flag=True, default=False,
- help='Run Flight integration tests')
-@click.option('--debug', is_flag=True, default=False,
- help='Run executables in debug mode as relevant')
-@click.option('--serial', is_flag=True, default=False,
- help='Run tests serially, rather than in parallel')
-@click.option('--tempdir', default=None,
- help=('Directory to use for writing '
- 'integration test temporary files'))
-@click.option('stop_on_error', '-x', '--stop-on-error',
- is_flag=True, default=False,
- help='Stop on first error')
-@click.option('--gold-dirs', multiple=True,
- help="gold integration test file paths")
-@click.option('-k', '--match',
- help=("Substring for test names to include in run, "
- "e.g. -k primitive"))
-def integration(with_all=False, random_seed=12345, **args):
- from .integration.runner import write_js_test_json, run_all_tests
- import numpy as np
-
- # FIXME(bkietz) Include help strings for individual testers.
- # For example, CPPTester's ARROW_CPP_EXE_PATH environment variable.
-
- # Make runs involving data generation deterministic
- np.random.seed(random_seed)
-
- gen_path = args['write_generated_json']
-
- languages = ['cpp', 'java', 'js', 'go', 'rust']
-
- enabled_languages = 0
- for lang in languages:
- param = 'with_{}'.format(lang)
- if with_all:
- args[param] = with_all
-
- if args[param]:
- enabled_languages += 1
-
- if gen_path:
- try:
- os.makedirs(gen_path)
- except OSError as e:
- if e.errno != errno.EEXIST:
- raise
- write_js_test_json(gen_path)
- else:
- if enabled_languages == 0:
- raise Exception("Must enable at least 1 language to test")
- run_all_tests(**args)
-
-
-@archery.command()
-@click.option('--event-name', '-n', required=True)
-@click.option('--event-payload', '-p', type=click.File('r', encoding='utf8'),
- default='-', required=True)
-@click.option('--arrow-token', envvar='ARROW_GITHUB_TOKEN',
- help='OAuth token for responding comment in the arrow repo')
-@click.option('--crossbow-token', '-ct', envvar='CROSSBOW_GITHUB_TOKEN',
- help='OAuth token for pushing to the crossow repository')
-def trigger_bot(event_name, event_payload, arrow_token, crossbow_token):
- from .bot import CommentBot, actions
-
- event_payload = json.loads(event_payload.read())
-
- bot = CommentBot(name='github-actions', handler=actions, token=arrow_token)
- bot.handle(event_name, event_payload)
-
-
-def _mock_compose_calls(compose):
- from types import MethodType
- from subprocess import CompletedProcess
-
- def _mock(compose, executable):
- def _execute(self, *args, **kwargs):
- params = ['{}={}'.format(k, v)
- for k, v in self.config.params.items()]
- command = ' '.join(params + [executable] + list(args))
- click.echo(command)
- return CompletedProcess([], 0)
- return MethodType(_execute, compose)
-
- compose._execute_docker = _mock(compose, executable='docker')
- compose._execute_compose = _mock(compose, executable='docker-compose')
-
-
-@archery.group('docker')
-@click.option("--src", metavar="<arrow_src>", default=None,
- callback=validate_arrow_sources,
- help="Specify Arrow source directory.")
-@click.option('--dry-run/--execute', default=False,
- help="Display the docker-compose commands instead of executing "
- "them.")
-@click.pass_obj
-def docker_compose(obj, src, dry_run):
- """Interact with docker-compose based builds."""
- from .docker import DockerCompose
-
- config_path = src.path / 'docker-compose.yml'
- if not config_path.exists():
- raise click.ClickException(
- "Docker compose configuration cannot be found in directory {}, "
- "try to pass the arrow source directory explicitly.".format(src)
- )
-
- # take the docker-compose parameters like PYTHON, PANDAS, UBUNTU from the
- # environment variables to keep the usage similar to docker-compose
- compose = DockerCompose(config_path, params=os.environ)
- if dry_run:
- _mock_compose_calls(compose)
- obj['compose'] = compose
-
-
-@docker_compose.command('build')
-@click.argument('image')
-@click.option('--force-pull/--no-pull', default=True,
- help="Whether to force pull the image and its ancestor images")
-@click.option('--using-docker-cli', default=False, is_flag=True,
- envvar='ARCHERY_USE_DOCKER_CLI',
- help="Use docker CLI directly for building instead of calling "
- "docker-compose. This may help to reuse cached layers.")
-@click.option('--using-docker-buildx', default=False, is_flag=True,
- envvar='ARCHERY_USE_DOCKER_BUILDX',
- help="Use buildx with docker CLI directly for building instead "
- "of calling docker-compose or the plain docker build "
- "command. This option makes the build cache reusable "
- "across hosts.")
-@click.option('--use-cache/--no-cache', default=True,
- help="Whether to use cache when building the image and its "
- "ancestor images")
-@click.option('--use-leaf-cache/--no-leaf-cache', default=True,
- help="Whether to use cache when building only the (leaf) image "
- "passed as the argument. To disable caching for both the "
- "image and its ancestors use --no-cache option.")
-@click.pass_obj
-def docker_compose_build(obj, image, *, force_pull, using_docker_cli,
- using_docker_buildx, use_cache, use_leaf_cache):
- """
- Execute docker-compose builds.
- """
- from .docker import UndefinedImage
-
- compose = obj['compose']
-
- using_docker_cli |= using_docker_buildx
- try:
- if force_pull:
- compose.pull(image, pull_leaf=use_leaf_cache,
- using_docker=using_docker_cli)
- compose.build(image, use_cache=use_cache,
- use_leaf_cache=use_leaf_cache,
- using_docker=using_docker_cli,
- using_buildx=using_docker_buildx)
- except UndefinedImage as e:
- raise click.ClickException(
- "There is no service/image defined in docker-compose.yml with "
- "name: {}".format(str(e))
- )
- except RuntimeError as e:
- raise click.ClickException(str(e))
-
-
-@docker_compose.command('run')
-@click.argument('image')
-@click.argument('command', required=False, default=None)
-@click.option('--env', '-e', multiple=True,
- help="Set environment variable within the container")
-@click.option('--user', '-u', default=None,
- help="Username or UID to run the container with")
-@click.option('--force-pull/--no-pull', default=True,
- help="Whether to force pull the image and its ancestor images")
-@click.option('--force-build/--no-build', default=True,
- help="Whether to force build the image and its ancestor images")
-@click.option('--build-only', default=False, is_flag=True,
- help="Pull and/or build the image, but do not run it")
-@click.option('--using-docker-cli', default=False, is_flag=True,
- envvar='ARCHERY_USE_DOCKER_CLI',
- help="Use docker CLI directly for building instead of calling "
- "docker-compose. This may help to reuse cached layers.")
-@click.option('--using-docker-buildx', default=False, is_flag=True,
- envvar='ARCHERY_USE_DOCKER_BUILDX',
- help="Use buildx with docker CLI directly for building instead "
- "of calling docker-compose or the plain docker build "
- "command. This option makes the build cache reusable "
- "across hosts.")
-@click.option('--use-cache/--no-cache', default=True,
- help="Whether to use cache when building the image and its "
- "ancestor images")
-@click.option('--use-leaf-cache/--no-leaf-cache', default=True,
- help="Whether to use cache when building only the (leaf) image "
- "passed as the argument. To disable caching for both the "
- "image and its ancestors use --no-cache option.")
-@click.option('--volume', '-v', multiple=True,
- help="Set volume within the container")
-@click.pass_obj
-def docker_compose_run(obj, image, command, *, env, user, force_pull,
- force_build, build_only, using_docker_cli,
- using_docker_buildx, use_cache,
- use_leaf_cache, volume):
- """Execute docker-compose builds.
-
- To see the available builds run `archery docker images`.
-
- Examples:
-
- # execute a single build
- archery docker run conda-python
-
- # execute the builds but disable the image pulling
- archery docker run --no-cache conda-python
-
- # pass a docker-compose parameter, like the python version
- PYTHON=3.8 archery docker run conda-python
-
- # disable the cache only for the leaf image
- PANDAS=master archery docker run --no-leaf-cache conda-python-pandas
-
- # entirely skip building the image
- archery docker run --no-pull --no-build conda-python
-
- # pass runtime parameters via docker environment variables
- archery docker run -e CMAKE_BUILD_TYPE=release ubuntu-cpp
-
- # set a volume
- archery docker run -v $PWD/build:/build ubuntu-cpp
-
- # starting an interactive bash session for debugging
- archery docker run ubuntu-cpp bash
- """
- from .docker import UndefinedImage
-
- compose = obj['compose']
- using_docker_cli |= using_docker_buildx
-
- env = dict(kv.split('=', 1) for kv in env)
- try:
- if force_pull:
- compose.pull(image, pull_leaf=use_leaf_cache,
- using_docker=using_docker_cli)
- if force_build:
- compose.build(image, use_cache=use_cache,
- use_leaf_cache=use_leaf_cache,
- using_docker=using_docker_cli,
- using_buildx=using_docker_buildx)
- if build_only:
- return
- compose.run(
- image,
- command=command,
- env=env,
- user=user,
- using_docker=using_docker_cli,
- volumes=volume
- )
- except UndefinedImage as e:
- raise click.ClickException(
- "There is no service/image defined in docker-compose.yml with "
- "name: {}".format(str(e))
- )
- except RuntimeError as e:
- raise click.ClickException(str(e))
-
-
-@docker_compose.command('push')
-@click.argument('image')
-@click.option('--user', '-u', required=False, envvar='ARCHERY_DOCKER_USER',
- help='Docker repository username')
-@click.option('--password', '-p', required=False,
- envvar='ARCHERY_DOCKER_PASSWORD',
- help='Docker repository password')
-@click.option('--using-docker-cli', default=False, is_flag=True,
- help="Use docker CLI directly for building instead of calling "
- "docker-compose. This may help to reuse cached layers.")
-@click.pass_obj
-def docker_compose_push(obj, image, user, password, using_docker_cli):
- """Push the generated docker-compose image."""
- compose = obj['compose']
- compose.push(image, user=user, password=password,
- using_docker=using_docker_cli)
-
-
-@docker_compose.command('images')
-@click.pass_obj
-def docker_compose_images(obj):
- """List the available docker-compose images."""
- compose = obj['compose']
- click.echo('Available images:')
- for image in compose.images():
- click.echo(' - {}'.format(image))
-
-
-@archery.group('release')
-@click.option("--src", metavar="<arrow_src>", default=None,
- callback=validate_arrow_sources,
- help="Specify Arrow source directory.")
-@click.option("--jira-cache", type=click.Path(), default=None,
- help="File path to cache queried JIRA issues per version.")
-@click.pass_obj
-def release(obj, src, jira_cache):
- """Release releated commands."""
- from .release import Jira, CachedJira
-
- jira = Jira()
- if jira_cache is not None:
- jira = CachedJira(jira_cache, jira=jira)
-
- obj['jira'] = jira
- obj['repo'] = src.path
-
-
-@release.command('curate')
-@click.argument('version')
-@click.pass_obj
-def release_curate(obj, version):
- """Release curation."""
- from .release import Release
-
- release = Release.from_jira(version, jira=obj['jira'], repo=obj['repo'])
- curation = release.curate()
-
- click.echo(curation.render('console'))
-
-
-@release.group('changelog')
-def release_changelog():
- """Release changelog."""
- pass
-
-
-@release_changelog.command('add')
-@click.argument('version')
-@click.pass_obj
-def release_changelog_add(obj, version):
- """Prepend the changelog with the current release"""
- from .release import Release
-
- jira, repo = obj['jira'], obj['repo']
-
- # just handle the current version
- release = Release.from_jira(version, jira=jira, repo=repo)
- if release.is_released:
- raise ValueError('This version has been already released!')
-
- changelog = release.changelog()
- changelog_path = pathlib.Path(repo) / 'CHANGELOG.md'
-
- current_content = changelog_path.read_text()
- new_content = changelog.render('markdown') + current_content
-
- changelog_path.write_text(new_content)
- click.echo("CHANGELOG.md is updated!")
-
-
-@release_changelog.command('generate')
-@click.argument('version')
-@click.argument('output', type=click.File('w', encoding='utf8'), default='-')
-@click.pass_obj
-def release_changelog_generate(obj, version, output):
- """Generate the changelog of a specific release."""
- from .release import Release
-
- jira, repo = obj['jira'], obj['repo']
-
- # just handle the current version
- release = Release.from_jira(version, jira=jira, repo=repo)
-
- changelog = release.changelog()
- output.write(changelog.render('markdown'))
-
-
-@release_changelog.command('regenerate')
-@click.pass_obj
-def release_changelog_regenerate(obj):
- """Regeneretate the whole CHANGELOG.md file"""
- from .release import Release
-
- jira, repo = obj['jira'], obj['repo']
- changelogs = []
-
- for version in jira.arrow_versions():
- if not version.released:
- continue
- release = Release.from_jira(version, jira=jira, repo=repo)
- click.echo('Querying changelog for version: {}'.format(version))
- changelogs.append(release.changelog())
-
- click.echo('Rendering new CHANGELOG.md file...')
- changelog_path = pathlib.Path(repo) / 'CHANGELOG.md'
- with changelog_path.open('w') as fp:
- for cl in changelogs:
- fp.write(cl.render('markdown'))
-
-
-@release.command('cherry-pick')
-@click.argument('version')
-@click.option('--dry-run/--execute', default=True,
- help="Display the git commands instead of executing them.")
-@click.option('--recreate/--continue', default=True,
- help="Recreate the maintenance branch or only apply unapplied "
- "patches.")
-@click.pass_obj
-def release_cherry_pick(obj, version, dry_run, recreate):
- """
- Cherry pick commits.
- """
- from .release import Release, MinorRelease, PatchRelease
-
- release = Release.from_jira(version, jira=obj['jira'], repo=obj['repo'])
- if not isinstance(release, (MinorRelease, PatchRelease)):
- raise click.UsageError('Cherry-pick command only supported for minor '
- 'and patch releases')
-
- if not dry_run:
- release.cherry_pick_commits(recreate_branch=recreate)
- click.echo('Executed the following commands:\n')
-
- click.echo(
- 'git checkout {} -b {}'.format(release.previous.tag, release.branch)
- )
- for commit in release.commits_to_pick():
- click.echo('git cherry-pick {}'.format(commit.hexsha))
-
-
-try:
- from .crossbow.cli import crossbow # noqa
-except ImportError as exc:
- missing_package = exc.name
-
- @archery.command(
- 'crossbow',
- context_settings={"ignore_unknown_options": True}
- )
- def crossbow():
- raise click.ClickException(
- "Couldn't import crossbow because of missing dependency: {}"
- .format(missing_package)
- )
-else:
- archery.add_command(crossbow)
-
-
-if __name__ == "__main__":
- archery(obj={})
diff --git a/dev/archery/archery/compat.py b/dev/archery/archery/compat.py
deleted file mode 100644
index 22cb9fc..0000000
--- a/dev/archery/archery/compat.py
+++ /dev/null
@@ -1,51 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-import pathlib
-
-
-def _is_path_like(path):
- # PEP519 filesystem path protocol is available from python 3.6, so pathlib
- # doesn't implement __fspath__ for earlier versions
- return (isinstance(path, str) or
- hasattr(path, '__fspath__') or
- isinstance(path, pathlib.Path))
-
-
-def _ensure_path(path):
- if isinstance(path, pathlib.Path):
- return path
- else:
- return pathlib.Path(_stringify_path(path))
-
-
-def _stringify_path(path):
- """
- Convert *path* to a string or unicode path if possible.
- """
- if isinstance(path, str):
- return path
-
- # checking whether path implements the filesystem protocol
- try:
- return path.__fspath__() # new in python 3.6
- except AttributeError:
- # fallback pathlib ckeck for earlier python versions than 3.6
- if isinstance(path, pathlib.Path):
- return str(path)
-
- raise TypeError("not a path-like object")
diff --git a/dev/archery/archery/crossbow/__init__.py b/dev/archery/archery/crossbow/__init__.py
deleted file mode 100644
index bc72e81..0000000
--- a/dev/archery/archery/crossbow/__init__.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-from .core import Config, Repo, Queue, Target, Job # noqa
-from .reports import CommentReport, ConsoleReport, EmailReport # noqa
diff --git a/dev/archery/archery/crossbow/cli.py b/dev/archery/archery/crossbow/cli.py
deleted file mode 100644
index 71c25e0..0000000
--- a/dev/archery/archery/crossbow/cli.py
+++ /dev/null
@@ -1,352 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-from pathlib import Path
-
-import click
-
-from .core import Config, Repo, Queue, Target, Job, CrossbowError
-from .reports import EmailReport, ConsoleReport
-from ..utils.source import ArrowSources
-
-
-_default_arrow_path = ArrowSources.find().path
-_default_queue_path = _default_arrow_path.parent / "crossbow"
-_default_config_path = _default_arrow_path / "dev" / "tasks" / "tasks.yml"
-
-
-@click.group()
-@click.option('--github-token', '-t', default=None,
- envvar="CROSSBOW_GITHUB_TOKEN",
- help='OAuth token for GitHub authentication')
-@click.option('--arrow-path', '-a',
- type=click.Path(), default=_default_arrow_path,
- help='Arrow\'s repository path. Defaults to the repository of '
- 'this script')
-@click.option('--queue-path', '-q',
- type=click.Path(), default=_default_queue_path,
- help='The repository path used for scheduling the tasks. '
- 'Defaults to crossbow directory placed next to arrow')
-@click.option('--queue-remote', '-qr', default=None,
- help='Force to use this remote URL for the Queue repository')
-@click.option('--output-file', metavar='<output>',
- type=click.File('w', encoding='utf8'), default='-',
- help='Capture output result into file.')
-@click.pass_context
-def crossbow(ctx, github_token, arrow_path, queue_path, queue_remote,
- output_file):
- """
- Schedule packaging tasks or nightly builds on CI services.
- """
- ctx.ensure_object(dict)
- ctx.obj['output'] = output_file
- ctx.obj['arrow'] = Repo(arrow_path)
- ctx.obj['queue'] = Queue(queue_path, remote_url=queue_remote,
- github_token=github_token, require_https=True)
-
-
-@crossbow.command()
-@click.option('--config-path', '-c',
- type=click.Path(exists=True), default=_default_config_path,
- help='Task configuration yml. Defaults to tasks.yml')
-@click.pass_obj
-def check_config(obj, config_path):
- # load available tasks configuration and groups from yaml
- config = Config.load_yaml(config_path)
- config.validate()
-
- output = obj['output']
- config.show(output)
-
-
-@crossbow.command()
-@click.argument('tasks', nargs=-1, required=False)
-@click.option('--group', '-g', 'groups', multiple=True,
- help='Submit task groups as defined in task.yml')
-@click.option('--param', '-p', 'params', multiple=True,
- help='Additional task parameters for rendering the CI templates')
-@click.option('--job-prefix', default='build',
- help='Arbitrary prefix for branch names, e.g. nightly')
-@click.option('--config-path', '-c',
- type=click.Path(exists=True), default=_default_config_path,
- help='Task configuration yml. Defaults to tasks.yml')
-@click.option('--arrow-version', '-v', default=None,
- help='Set target version explicitly.')
-@click.option('--arrow-remote', '-r', default=None,
- help='Set GitHub remote explicitly, which is going to be cloned '
- 'on the CI services. Note, that no validation happens '
- 'locally. Examples: https://github.com/apache/arrow or '
- 'https://github.com/kszucs/arrow.')
-@click.option('--arrow-branch', '-b', default=None,
- help='Give the branch name explicitly, e.g. master, ARROW-1949.')
-@click.option('--arrow-sha', '-t', default=None,
- help='Set commit SHA or Tag name explicitly, e.g. f67a515, '
- 'apache-arrow-0.11.1.')
-@click.option('--fetch/--no-fetch', default=True,
- help='Fetch references (branches and tags) from the remote')
-@click.option('--dry-run/--commit', default=False,
- help='Just display the rendered CI configurations without '
- 'committing them')
-@click.option('--no-push/--push', default=False,
- help='Don\'t push the changes')
-@click.pass_obj
-def submit(obj, tasks, groups, params, job_prefix, config_path, arrow_version,
- arrow_remote, arrow_branch, arrow_sha, fetch, dry_run, no_push):
- output = obj['output']
- queue, arrow = obj['queue'], obj['arrow']
-
- # load available tasks configuration and groups from yaml
- config = Config.load_yaml(config_path)
- try:
- config.validate()
- except CrossbowError as e:
- raise click.ClickException(str(e))
-
- # Override the detected repo url / remote, branch and sha - this aims to
- # make release procedure a bit simpler.
- # Note, that the target resivion's crossbow templates must be
- # compatible with the locally checked out version of crossbow (which is
- # in case of the release procedure), because the templates still
- # contain some business logic (dependency installation, deployments)
- # which will be reduced to a single command in the future.
- target = Target.from_repo(arrow, remote=arrow_remote, branch=arrow_branch,
- head=arrow_sha, version=arrow_version)
-
- # parse additional job parameters
- params = dict([p.split("=") for p in params])
-
- # instantiate the job object
- try:
- job = Job.from_config(config=config, target=target, tasks=tasks,
- groups=groups, params=params)
- except CrossbowError as e:
- raise click.ClickException(str(e))
-
- job.show(output)
- if dry_run:
- return
-
- if fetch:
- queue.fetch()
- queue.put(job, prefix=job_prefix)
-
- if no_push:
- click.echo('Branches and commits created but not pushed: `{}`'
- .format(job.branch))
- else:
- queue.push()
- click.echo('Pushed job identifier is: `{}`'.format(job.branch))
-
-
-@crossbow.command()
-@click.argument('task', required=True)
-@click.option('--config-path', '-c',
- type=click.Path(exists=True), default=_default_config_path,
- help='Task configuration yml. Defaults to tasks.yml')
-@click.option('--arrow-version', '-v', default=None,
- help='Set target version explicitly.')
-@click.option('--arrow-remote', '-r', default=None,
- help='Set GitHub remote explicitly, which is going to be cloned '
- 'on the CI services. Note, that no validation happens '
- 'locally. Examples: https://github.com/apache/arrow or '
- 'https://github.com/kszucs/arrow.')
-@click.option('--arrow-branch', '-b', default=None,
- help='Give the branch name explicitly, e.g. master, ARROW-1949.')
-@click.option('--arrow-sha', '-t', default=None,
- help='Set commit SHA or Tag name explicitly, e.g. f67a515, '
- 'apache-arrow-0.11.1.')
-@click.option('--param', '-p', 'params', multiple=True,
- help='Additional task parameters for rendering the CI templates')
-@click.pass_obj
-def render(obj, task, config_path, arrow_version, arrow_remote, arrow_branch,
- arrow_sha, params):
- """
- Utility command to check the rendered CI templates.
- """
- from .core import _flatten
-
- def highlight(code):
- try:
- from pygments import highlight
- from pygments.lexers import YamlLexer
- from pygments.formatters import TerminalFormatter
- return highlight(code, YamlLexer(), TerminalFormatter())
- except ImportError:
- return code
-
- arrow = obj['arrow']
-
- target = Target.from_repo(arrow, remote=arrow_remote, branch=arrow_branch,
- head=arrow_sha, version=arrow_version)
- config = Config.load_yaml(config_path)
- params = dict([p.split("=") for p in params])
- job = Job.from_config(config=config, target=target, tasks=[task],
- params=params)
-
- for task_name, rendered_files in job.render_tasks().items():
- for path, content in _flatten(rendered_files).items():
- click.echo('#' * 80)
- click.echo('### {:^72} ###'.format("/".join(path)))
- click.echo('#' * 80)
- click.echo(highlight(content))
-
-
-@crossbow.command()
-@click.argument('job-name', required=True)
-@click.option('--fetch/--no-fetch', default=True,
- help='Fetch references (branches and tags) from the remote')
-@click.pass_obj
-def status(obj, job_name, fetch):
- output = obj['output']
- queue = obj['queue']
- if fetch:
- queue.fetch()
- job = queue.get(job_name)
- ConsoleReport(job).show(output)
-
-
-@crossbow.command()
-@click.argument('prefix', required=True)
-@click.option('--fetch/--no-fetch', default=True,
- help='Fetch references (branches and tags) from the remote')
-@click.pass_obj
-def latest_prefix(obj, prefix, fetch):
- queue = obj['queue']
- if fetch:
- queue.fetch()
- latest = queue.latest_for_prefix(prefix)
- click.echo(latest.branch)
-
-
-@crossbow.command()
-@click.argument('job-name', required=True)
-@click.option('--sender-name', '-n',
- help='Name to use for report e-mail.')
-@click.option('--sender-email', '-e',
- help='E-mail to use for report e-mail.')
-@click.option('--recipient-email', '-r',
- help='Where to send the e-mail report')
-@click.option('--smtp-user', '-u',
- help='E-mail address to use for SMTP login')
-@click.option('--smtp-password', '-P',
- help='SMTP password to use for report e-mail.')
-@click.option('--smtp-server', '-s', default='smtp.gmail.com',
- help='SMTP server to use for report e-mail.')
-@click.option('--smtp-port', '-p', default=465,
- help='SMTP port to use for report e-mail.')
-@click.option('--poll/--no-poll', default=False,
- help='Wait for completion if there are tasks pending')
-@click.option('--poll-max-minutes', default=180,
- help='Maximum amount of time waiting for job completion')
-@click.option('--poll-interval-minutes', default=10,
- help='Number of minutes to wait to check job status again')
-@click.option('--send/--dry-run', default=False,
- help='Just display the report, don\'t send it')
-@click.option('--fetch/--no-fetch', default=True,
- help='Fetch references (branches and tags) from the remote')
-@click.pass_obj
-def report(obj, job_name, sender_name, sender_email, recipient_email,
- smtp_user, smtp_password, smtp_server, smtp_port, poll,
- poll_max_minutes, poll_interval_minutes, send, fetch):
- """
- Send an e-mail report showing success/failure of tasks in a Crossbow run
- """
- output = obj['output']
- queue = obj['queue']
- if fetch:
- queue.fetch()
-
- job = queue.get(job_name)
- report = EmailReport(
- job=job,
- sender_name=sender_name,
- sender_email=sender_email,
- recipient_email=recipient_email
- )
-
- if poll:
- job.wait_until_finished(
- poll_max_minutes=poll_max_minutes,
- poll_interval_minutes=poll_interval_minutes
- )
-
- if send:
- report.send(
- smtp_user=smtp_user,
- smtp_password=smtp_password,
- smtp_server=smtp_server,
- smtp_port=smtp_port
- )
- else:
- report.show(output)
-
-
-@crossbow.command()
-@click.argument('job-name', required=True)
-@click.option('-t', '--target-dir',
- default=_default_arrow_path / 'packages',
- type=click.Path(file_okay=False, dir_okay=True),
- help='Directory to download the build artifacts')
-@click.option('--dry-run/--execute', default=False,
- help='Just display process, don\'t download anything')
-@click.option('--fetch/--no-fetch', default=True,
- help='Fetch references (branches and tags) from the remote')
-@click.pass_obj
-def download_artifacts(obj, job_name, target_dir, dry_run, fetch):
- """Download build artifacts from GitHub releases"""
- output = obj['output']
-
- # fetch the queue repository
- queue = obj['queue']
- if fetch:
- queue.fetch()
-
- # query the job's artifacts
- job = queue.get(job_name)
-
- # create directory to download the assets to
- target_dir = Path(target_dir).absolute() / job_name
- target_dir.mkdir(parents=True, exist_ok=True)
-
- # download the assets while showing the job status
- def asset_callback(task_name, task, asset):
- if asset is not None:
- path = target_dir / task_name / asset.name
- path.parent.mkdir(exist_ok=True)
- if not dry_run:
- asset.download(path)
-
- click.echo('Downloading {}\'s artifacts.'.format(job_name))
- click.echo('Destination directory is {}'.format(target_dir))
- click.echo()
-
- report = ConsoleReport(job)
- report.show(output, asset_callback=asset_callback)
-
-
-@crossbow.command()
-@click.option('--sha', required=True, help='Target committish')
-@click.option('--tag', required=True, help='Target tag')
-@click.option('--method', default='curl', help='Use cURL to upload')
-@click.option('--pattern', '-p', 'patterns', required=True, multiple=True,
- help='File pattern to upload as assets')
-@click.pass_obj
-def upload_artifacts(obj, tag, sha, patterns, method):
- queue = obj['queue']
- queue.github_overwrite_release_assets(
- tag_name=tag, target_commitish=sha, method=method, patterns=patterns
- )
diff --git a/dev/archery/archery/crossbow/core.py b/dev/archery/archery/crossbow/core.py
deleted file mode 100644
index 9d3074a..0000000
--- a/dev/archery/archery/crossbow/core.py
+++ /dev/null
@@ -1,1162 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-import os
-import re
-import fnmatch
-import glob
-import time
-import logging
-import mimetypes
-import subprocess
-import textwrap
-from io import StringIO
-from pathlib import Path
-from datetime import date
-
-import jinja2
-from ruamel.yaml import YAML
-
-try:
- import github3
- _have_github3 = True
-except ImportError:
- github3 = object
- _have_github3 = False
-
-try:
- import pygit2
-except ImportError:
- PygitRemoteCallbacks = object
-else:
- PygitRemoteCallbacks = pygit2.RemoteCallbacks
-
-from ..utils.source import ArrowSources
-
-
-for pkg in ["requests", "urllib3", "github3"]:
- logging.getLogger(pkg).setLevel(logging.WARNING)
-
-logger = logging.getLogger("crossbow")
-
-
-class CrossbowError(Exception):
- pass
-
-
-def _flatten(mapping):
- """Converts a hierarchical mapping to a flat dictionary"""
- result = {}
- for k, v in mapping.items():
- if isinstance(v, dict):
- for ik, iv in _flatten(v).items():
- ik = ik if isinstance(ik, tuple) else (ik,)
- result[(k,) + ik] = iv
- elif isinstance(v, list):
- for ik, iv in enumerate(_flatten(v)):
- ik = ik if isinstance(ik, tuple) else (ik,)
- result[(k,) + ik] = iv
- else:
- result[(k,)] = v
- return result
-
-
-def _unflatten(mapping):
- """Converts a flat tuple => object mapping to hierarchical one"""
- result = {}
- for path, value in mapping.items():
- parents, leaf = path[:-1], path[-1]
- # create the hierarchy until we reach the leaf value
- temp = result
- for parent in parents:
- temp.setdefault(parent, {})
- temp = temp[parent]
- # set the leaf value
- temp[leaf] = value
-
- return result
-
-
-def _unflatten_tree(files):
- """Converts a flat path => object mapping to a hierarchical directories
-
- Input:
- {
- 'path/to/file.a': a_content,
- 'path/to/file.b': b_content,
- 'path/file.c': c_content
- }
- Output:
- {
- 'path': {
- 'to': {
- 'file.a': a_content,
- 'file.b': b_content
- },
- 'file.c': c_content
- }
- }
- """
- files = {tuple(k.split('/')): v for k, v in files.items()}
- return _unflatten(files)
-
-
-def _render_jinja_template(searchpath, template, params):
- def format_all(items, pattern):
- return [pattern.format(item) for item in items]
-
- loader = jinja2.FileSystemLoader(searchpath)
- env = jinja2.Environment(loader=loader, trim_blocks=True,
- lstrip_blocks=True,
- undefined=jinja2.StrictUndefined)
- env.filters['format_all'] = format_all
- template = env.get_template(template)
- return template.render(**params)
-
-
-# configurations for setting up branch skipping
-# - appveyor has a feature to skip builds without an appveyor.yml
-# - travis reads from the master branch and applies the rules
-# - circle requires the configuration to be present on all branch, even ones
-# that are configured to be skipped
-# - azure skips branches without azure-pipelines.yml by default
-# - github skips branches without .github/workflows/ by default
-
-_default_travis_yml = """
-branches:
- only:
- - master
- - /.*-travis-.*/
-
-os: linux
-dist: trusty
-language: generic
-"""
-
-_default_circle_yml = """
-version: 2
-
-jobs:
- build:
- machine: true
-
-workflows:
- version: 2
- build:
- jobs:
- - build:
- filters:
- branches:
- only:
- - /.*-circle-.*/
-"""
-
-_default_tree = {
- '.travis.yml': _default_travis_yml,
- '.circleci/config.yml': _default_circle_yml
-}
-
-
-class GitRemoteCallbacks(PygitRemoteCallbacks):
-
- def __init__(self, token):
- self.token = token
- self.attempts = 0
- super().__init__()
-
- def push_update_reference(self, refname, message):
- pass
-
- def update_tips(self, refname, old, new):
- pass
-
- def credentials(self, url, username_from_url, allowed_types):
- # its a libgit2 bug, that it infinitely retries the authentication
- self.attempts += 1
-
- if self.attempts >= 5:
- # pygit2 doesn't propagate the exception properly
- msg = 'Wrong oauth personal access token'
- print(msg)
- raise CrossbowError(msg)
-
- if allowed_types & pygit2.credentials.GIT_CREDTYPE_USERPASS_PLAINTEXT:
- return pygit2.UserPass(self.token, 'x-oauth-basic')
- else:
- return None
-
-
-def _git_ssh_to_https(url):
- return url.replace('git@github.com:', 'https://github.com/')
-
-
-class Repo:
- """
- Base class for interaction with local git repositories
-
- A high level wrapper used for both reading revision information from
- arrow's repository and pushing continuous integration tasks to the queue
- repository.
-
- Parameters
- ----------
- require_https : boolean, default False
- Raise exception for SSH origin URLs
- """
-
- def __init__(self, path, github_token=None, remote_url=None,
- require_https=False):
- self.path = Path(path)
- self.github_token = github_token
- self.require_https = require_https
- self._remote_url = remote_url
- self._pygit_repo = None
- self._github_repo = None # set by as_github_repo()
- self._updated_refs = []
-
- def __str__(self):
- tpl = textwrap.dedent('''
- Repo: {remote}@{branch}
- Commit: {head}
- ''')
- return tpl.format(
- remote=self.remote_url,
- branch=self.branch.branch_name,
- head=self.head
- )
-
- @property
- def repo(self):
- if self._pygit_repo is None:
- self._pygit_repo = pygit2.Repository(str(self.path))
- return self._pygit_repo
-
- @property
- def origin(self):
- remote = self.repo.remotes['origin']
- if self.require_https and remote.url.startswith('git@github.com'):
- raise CrossbowError("Change SSH origin URL to HTTPS to use "
- "Crossbow: {}".format(remote.url))
- return remote
-
- def fetch(self):
- refspec = '+refs/heads/*:refs/remotes/origin/*'
- self.origin.fetch([refspec])
-
- def push(self, refs=None, github_token=None):
- github_token = github_token or self.github_token
- if github_token is None:
- raise RuntimeError(
- 'Could not determine GitHub token. Please set the '
- 'CROSSBOW_GITHUB_TOKEN environment variable to a '
- 'valid GitHub access token or pass one to --github-token.'
- )
- callbacks = GitRemoteCallbacks(github_token)
- refs = refs or []
- try:
- self.origin.push(refs + self._updated_refs, callbacks=callbacks)
- except pygit2.GitError:
- raise RuntimeError('Failed to push updated references, '
- 'potentially because of credential issues: {}'
- .format(self._updated_refs))
- else:
- self.updated_refs = []
-
- @property
- def head(self):
- """Currently checked out commit's sha"""
- return self.repo.head
-
- @property
- def branch(self):
- """Currently checked out branch"""
- try:
- return self.repo.branches[self.repo.head.shorthand]
- except KeyError:
- return None # detached
-
- @property
- def remote(self):
- """Currently checked out branch's remote counterpart"""
- try:
- return self.repo.remotes[self.branch.upstream.remote_name]
- except (AttributeError, KeyError):
- return None # cannot detect
-
- @property
- def remote_url(self):
- """Currently checked out branch's remote counterpart URL
-
- If an SSH github url is set, it will be replaced by the https
- equivalent usable with GitHub OAuth token.
- """
- try:
- return self._remote_url or _git_ssh_to_https(self.remote.url)
- except AttributeError:
- return None
-
- @property
- def user_name(self):
- try:
- return next(self.repo.config.get_multivar('user.name'))
- except StopIteration:
- return os.environ.get('GIT_COMMITTER_NAME', 'unknown')
-
- @property
- def user_email(self):
- try:
- return next(self.repo.config.get_multivar('user.email'))
- except StopIteration:
- return os.environ.get('GIT_COMMITTER_EMAIL', 'unknown')
-
- @property
- def signature(self):
- return pygit2.Signature(self.user_name, self.user_email,
- int(time.time()))
-
- def create_tree(self, files):
- builder = self.repo.TreeBuilder()
-
- for filename, content in files.items():
- if isinstance(content, dict):
- # create a subtree
- tree_id = self.create_tree(content)
- builder.insert(filename, tree_id, pygit2.GIT_FILEMODE_TREE)
- else:
- # create a file
- blob_id = self.repo.create_blob(content)
- builder.insert(filename, blob_id, pygit2.GIT_FILEMODE_BLOB)
-
- tree_id = builder.write()
- return tree_id
-
- def create_commit(self, files, parents=None, message='',
- reference_name=None):
- if parents is None:
- # by default use the main branch as the base of the new branch
- # required to reuse github actions cache across crossbow tasks
- commit, _ = self.repo.resolve_refish("master")
- parents = [commit.id]
- tree_id = self.create_tree(files)
-
- author = committer = self.signature
- commit_id = self.repo.create_commit(reference_name, author, committer,
- message, tree_id, parents)
- return self.repo[commit_id]
-
- def create_branch(self, branch_name, files, parents=None, message='',
- signature=None):
- # create commit with the passed tree
- commit = self.create_commit(files, parents=parents, message=message)
-
- # create branch pointing to the previously created commit
- branch = self.repo.create_branch(branch_name, commit)
-
- # append to the pushable references
- self._updated_refs.append('refs/heads/{}'.format(branch_name))
-
- return branch
-
- def create_tag(self, tag_name, commit_id, message=''):
- tag_id = self.repo.create_tag(tag_name, commit_id,
- pygit2.GIT_OBJ_COMMIT, self.signature,
- message)
-
- # append to the pushable references
- self._updated_refs.append('refs/tags/{}'.format(tag_name))
-
- return self.repo[tag_id]
-
- def file_contents(self, commit_id, file):
- commit = self.repo[commit_id]
- entry = commit.tree[file]
- blob = self.repo[entry.id]
- return blob.data
-
- def _parse_github_user_repo(self):
- m = re.match(r'.*\/([^\/]+)\/([^\/\.]+)(\.git)?$', self.remote_url)
- if m is None:
- raise CrossbowError(
- "Unable to parse the github owner and repository from the "
- "repository's remote url '{}'".format(self.remote_url)
- )
- user, repo = m.group(1), m.group(2)
- return user, repo
-
- def as_github_repo(self, github_token=None):
- """Converts it to a repository object which wraps the GitHub API"""
- if self._github_repo is None:
- if not _have_github3:
- raise ImportError('Must install github3.py')
- github_token = github_token or self.github_token
- username, reponame = self._parse_github_user_repo()
- session = github3.session.GitHubSession(
- default_connect_timeout=10,
- default_read_timeout=30
- )
- github = github3.GitHub(session=session)
- github.login(token=github_token)
- self._github_repo = github.repository(username, reponame)
- return self._github_repo
-
- def github_commit(self, sha):
- repo = self.as_github_repo()
- return repo.commit(sha)
-
- def github_release(self, tag):
- repo = self.as_github_repo()
- try:
- return repo.release_from_tag(tag)
- except github3.exceptions.NotFoundError:
- return None
-
- def github_upload_asset_requests(self, release, path, name, mime,
- max_retries=None, retry_backoff=None):
- if max_retries is None:
- max_retries = int(os.environ.get('CROSSBOW_MAX_RETRIES', 8))
- if retry_backoff is None:
- retry_backoff = int(os.environ.get('CROSSBOW_RETRY_BACKOFF', 5))
-
- for i in range(max_retries):
- try:
- with open(path, 'rb') as fp:
- result = release.upload_asset(name=name, asset=fp,
- content_type=mime)
- except github3.exceptions.ResponseError as e:
- logger.error('Attempt {} has failed with message: {}.'
- .format(i + 1, str(e)))
- logger.error('Error message {}'.format(e.msg))
- logger.error('List of errors provided by Github:')
- for err in e.errors:
- logger.error(' - {}'.format(err))
-
- if e.code == 422:
- # 422 Validation Failed, probably raised because
- # ReleaseAsset already exists, so try to remove it before
- # reattempting the asset upload
- for asset in release.assets():
- if asset.name == name:
- logger.info('Release asset {} already exists, '
- 'removing it...'.format(name))
- asset.delete()
- logger.info('Asset {} removed.'.format(name))
- break
- except github3.exceptions.ConnectionError as e:
- logger.error('Attempt {} has failed with message: {}.'
- .format(i + 1, str(e)))
- else:
- logger.info('Attempt {} has finished.'.format(i + 1))
- return result
-
- time.sleep(retry_backoff)
-
- raise RuntimeError('Github asset uploading has failed!')
-
- def github_upload_asset_curl(self, release, path, name, mime):
- upload_url, _ = release.upload_url.split('{?')
- upload_url += '?name={}'.format(name)
-
- command = [
- 'curl',
- '--fail',
- '-H', "Authorization: token {}".format(self.github_token),
- '-H', "Content-Type: {}".format(mime),
- '--data-binary', '@{}'.format(path),
- upload_url
- ]
- return subprocess.run(command, shell=False, check=True)
-
- def github_overwrite_release_assets(self, tag_name, target_commitish,
- patterns, method='requests'):
- # Since github has changed something the asset uploading via requests
- # got instable, so prefer the cURL alternative.
- # Potential cause:
- # sigmavirus24/github3.py/issues/779#issuecomment-379470626
- repo = self.as_github_repo()
- if not tag_name:
- raise CrossbowError('Empty tag name')
- if not target_commitish:
- raise CrossbowError('Empty target commit for the release tag')
-
- # remove the whole release if it already exists
- try:
- release = repo.release_from_tag(tag_name)
- except github3.exceptions.NotFoundError:
- pass
- else:
- release.delete()
-
- release = repo.create_release(tag_name, target_commitish)
- for pattern in patterns:
- for path in glob.glob(pattern, recursive=True):
- name = os.path.basename(path)
- size = os.path.getsize(path)
- mime = mimetypes.guess_type(name)[0] or 'application/zip'
-
- logger.info(
- 'Uploading asset `{}` with mimetype {} and size {}...'
- .format(name, mime, size)
- )
-
- if method == 'requests':
- self.github_upload_asset_requests(release, path, name=name,
- mime=mime)
- elif method == 'curl':
- self.github_upload_asset_curl(release, path, name=name,
- mime=mime)
- else:
- raise CrossbowError(
- 'Unsupported upload method {}'.format(method)
- )
-
-
-class Queue(Repo):
-
- def _latest_prefix_id(self, prefix):
- pattern = re.compile(r'[\w\/-]*{}-(\d+)'.format(prefix))
- matches = list(filter(None, map(pattern.match, self.repo.branches)))
- if matches:
- latest = max(int(m.group(1)) for m in matches)
- else:
- latest = -1
- return latest
-
- def _next_job_id(self, prefix):
- """Auto increments the branch's identifier based on the prefix"""
- latest_id = self._latest_prefix_id(prefix)
- return '{}-{}'.format(prefix, latest_id + 1)
-
- def latest_for_prefix(self, prefix):
- latest_id = self._latest_prefix_id(prefix)
- if latest_id < 0:
- raise RuntimeError(
- 'No job has been submitted with prefix {} yet'.format(prefix)
- )
- job_name = '{}-{}'.format(prefix, latest_id)
- return self.get(job_name)
-
- def date_of(self, job):
- # it'd be better to bound to the queue repository on deserialization
- # and reorganize these methods to Job
- branch_name = 'origin/{}'.format(job.branch)
- branch = self.repo.branches[branch_name]
- commit = self.repo[branch.target]
- return date.fromtimestamp(commit.commit_time)
-
- def jobs(self, pattern):
- """Return jobs sorted by its identifier in reverse order"""
- job_names = []
- for name in self.repo.branches.remote:
- origin, name = name.split('/', 1)
- result = re.match(pattern, name)
- if result:
- job_names.append(name)
-
- for name in sorted(job_names, reverse=True):
- yield self.get(name)
-
- def get(self, job_name):
- branch_name = 'origin/{}'.format(job_name)
- branch = self.repo.branches[branch_name]
- try:
- content = self.file_contents(branch.target, 'job.yml')
- except KeyError:
- raise CrossbowError(
- 'No job is found with name: {}'.format(job_name)
- )
-
- buffer = StringIO(content.decode('utf-8'))
- job = yaml.load(buffer)
- job.queue = self
- return job
-
- def put(self, job, prefix='build'):
- if not isinstance(job, Job):
- raise CrossbowError('`job` must be an instance of Job')
- if job.branch is not None:
- raise CrossbowError('`job.branch` is automatically generated, '
- 'thus it must be blank')
-
- if job.target.remote is None:
- raise CrossbowError(
- 'Cannot determine git remote for the Arrow repository to '
- 'clone or push to, try to push the `{}` branch first to have '
- 'a remote tracking counterpart.'.format(job.target.branch)
- )
- if job.target.branch is None:
- raise CrossbowError(
- 'Cannot determine the current branch of the Arrow repository '
- 'to clone or push to, perhaps it is in detached HEAD state. '
- 'Please checkout a branch.'
- )
-
- # auto increment and set next job id, e.g. build-85
- job._queue = self
- job.branch = self._next_job_id(prefix)
-
- # create tasks' branches
- for task_name, task in job.tasks.items():
- # adding CI's name to the end of the branch in order to use skip
- # patterns on travis and circleci
- task.branch = '{}-{}-{}'.format(job.branch, task.ci, task_name)
- params = {
- **job.params,
- "arrow": job.target,
- "queue_remote_url": self.remote_url
- }
- files = task.render_files(job.template_searchpath, params=params)
- branch = self.create_branch(task.branch, files=files)
- self.create_tag(task.tag, branch.target)
- task.commit = str(branch.target)
-
- # create job's branch with its description
- return self.create_branch(job.branch, files=job.render_files())
-
-
-def get_version(root, **kwargs):
- """
- Parse function for setuptools_scm that ignores tags for non-C++
- subprojects, e.g. apache-arrow-js-XXX tags.
- """
- from setuptools_scm.git import parse as parse_git_version
-
- # query the calculated version based on the git tags
- kwargs['describe_command'] = (
- 'git describe --dirty --tags --long --match "apache-arrow-[0-9].*"'
- )
- version = parse_git_version(root, **kwargs)
-
- # increment the minor version, because there can be patch releases created
- # from maintenance branches where the tags are unreachable from the
- # master's HEAD, so the git command above generates 0.17.0.dev300 even if
- # arrow has a never 0.17.1 patch release
- pattern = r"^(\d+)\.(\d+)\.(\d+)$"
- match = re.match(pattern, str(version.tag))
- major, minor, patch = map(int, match.groups())
-
- # the bumped version number after 0.17.x will be 0.18.0.dev300
- return "{}.{}.{}.dev{}".format(major, minor + 1, patch, version.distance)
-
-
-class Serializable:
-
- @classmethod
- def to_yaml(cls, representer, data):
- tag = '!{}'.format(cls.__name__)
- dct = {k: v for k, v in data.__dict__.items() if not k.startswith('_')}
- return representer.represent_mapping(tag, dct)
-
-
-class Target(Serializable):
- """
- Describes target repository and revision the builds run against
-
- This serializable data container holding information about arrow's
- git remote, branch, sha and version number as well as some metadata
- (currently only an email address where the notification should be sent).
- """
-
- def __init__(self, head, branch, remote, version, email=None):
- self.head = head
- self.email = email
- self.branch = branch
- self.remote = remote
- self.version = version
- self.no_rc_version = re.sub(r'-rc\d+\Z', '', version)
- # Semantic Versioning 1.0.0: https://semver.org/spec/v1.0.0.html
- #
- # > A pre-release version number MAY be denoted by appending an
- # > arbitrary string immediately following the patch version and a
- # > dash. The string MUST be comprised of only alphanumerics plus
- # > dash [0-9A-Za-z-].
- #
- # Example:
- #
- # '0.16.1.dev10' ->
- # '0.16.1-dev10'
- self.no_rc_semver_version = \
- re.sub(r'\.(dev\d+)\Z', r'-\1', self.no_rc_version)
-
- @classmethod
- def from_repo(cls, repo, head=None, branch=None, remote=None, version=None,
- email=None):
- """Initialize from a repository
-
- Optionally override detected remote, branch, head, and/or version.
- """
- assert isinstance(repo, Repo)
-
- if head is None:
- head = str(repo.head.target)
- if branch is None:
- branch = repo.branch.branch_name
- if remote is None:
- remote = repo.remote_url
- if version is None:
- version = get_version(repo.path)
- if email is None:
- email = repo.user_email
-
- return cls(head=head, email=email, branch=branch, remote=remote,
- version=version)
-
-
-class Task(Serializable):
- """
- Describes a build task and metadata required to render CI templates
-
- A task is represented as a single git commit and branch containing jinja2
- rendered files (currently appveyor.yml or .travis.yml configurations).
-
- A task can't be directly submitted to a queue, must belong to a job.
- Each task's unique identifier is its branch name, which is generated after
- submitting the job to a queue.
- """
-
- def __init__(self, ci, template, artifacts=None, params=None):
- assert ci in {
- 'circle',
- 'travis',
- 'appveyor',
- 'azure',
- 'github',
- 'drone',
- }
- self.ci = ci
- self.template = template
- self.artifacts = artifacts or []
- self.params = params or {}
- self.branch = None # filled after adding to a queue
- self.commit = None # filled after adding to a queue
- self._queue = None # set by the queue object after put or get
- self._status = None # status cache
- self._assets = None # assets cache
-
- def render_files(self, searchpath, params=None):
- params = {**self.params, **(params or {}), "task": self}
- try:
- rendered = _render_jinja_template(searchpath, self.template,
- params=params)
- except jinja2.TemplateError as e:
- raise RuntimeError(
- 'Failed to render template `{}` with {}: {}'.format(
- self.template, e.__class__.__name__, str(e)
- )
- )
-
- tree = {**_default_tree, self.filename: rendered}
- return _unflatten_tree(tree)
-
- @property
- def tag(self):
- return self.branch
-
- @property
- def filename(self):
- config_files = {
- 'circle': '.circleci/config.yml',
- 'travis': '.travis.yml',
- 'appveyor': 'appveyor.yml',
- 'azure': 'azure-pipelines.yml',
- 'github': '.github/workflows/crossbow.yml',
- 'drone': '.drone.yml',
- }
- return config_files[self.ci]
-
- def status(self, force_query=False):
- _status = getattr(self, '_status', None)
- if force_query or _status is None:
- github_commit = self._queue.github_commit(self.commit)
- self._status = TaskStatus(github_commit)
- return self._status
-
- def assets(self, force_query=False):
- _assets = getattr(self, '_assets', None)
- if force_query or _assets is None:
- github_release = self._queue.github_release(self.tag)
- self._assets = TaskAssets(github_release,
- artifact_patterns=self.artifacts)
- return self._assets
-
-
-class TaskStatus:
- """
- Combine the results from status and checks API to a single state.
-
- Azure pipelines uses checks API which doesn't provide a combined
- interface like status API does, so we need to manually combine
- both the commit statuses and the commit checks coming from
- different API endpoint
-
- Status.state: error, failure, pending or success, default pending
- CheckRun.status: queued, in_progress or completed, default: queued
- CheckRun.conclusion: success, failure, neutral, cancelled, timed_out
- or action_required, only set if
- CheckRun.status == 'completed'
-
- 1. Convert CheckRun's status and conclusion to one of Status.state
- 2. Merge the states based on the following rules:
- - failure if any of the contexts report as error or failure
- - pending if there are no statuses or a context is pending
- - success if the latest status for all contexts is success
- error otherwise.
-
- Parameters
- ----------
- commit : github3.Commit
- Commit to query the combined status for.
-
- Returns
- -------
- TaskStatus(
- combined_state='error|failure|pending|success',
- github_status='original github status object',
- github_check_runs='github checks associated with the commit',
- total_count='number of statuses and checks'
- )
- """
-
- def __init__(self, commit):
- status = commit.status()
- check_runs = list(commit.check_runs())
- states = [s.state for s in status.statuses]
-
- for check in check_runs:
- if check.status == 'completed':
- if check.conclusion in {'success', 'failure'}:
- states.append(check.conclusion)
- elif check.conclusion in {'cancelled', 'timed_out',
- 'action_required'}:
- states.append('error')
- # omit `neutral` conclusion
- else:
- states.append('pending')
-
- # it could be more effective, but the following is more descriptive
- combined_state = 'error'
- if len(states):
- if any(state in {'error', 'failure'} for state in states):
- combined_state = 'failure'
- elif any(state == 'pending' for state in states):
- combined_state = 'pending'
- elif all(state == 'success' for state in states):
- combined_state = 'success'
-
- # show link to the actual build, some of the CI providers implement
- # the statuses API others implement the checks API, so display both
- build_links = [s.target_url for s in status.statuses]
- build_links += [c.html_url for c in check_runs]
-
- self.combined_state = combined_state
- self.github_status = status
- self.github_check_runs = check_runs
- self.total_count = len(states)
- self.build_links = build_links
-
-
-class TaskAssets(dict):
-
- def __init__(self, github_release, artifact_patterns):
- # HACK(kszucs): don't expect uploaded assets of no atifacts were
- # defiened for the tasks in order to spare a bit of github rate limit
- if not artifact_patterns:
- return
-
- if github_release is None:
- github_assets = {} # no assets have been uploaded for the task
- else:
- github_assets = {a.name: a for a in github_release.assets()}
-
- for pattern in artifact_patterns:
- # artifact can be a regex pattern
- compiled = re.compile(pattern)
- matches = list(
- filter(None, map(compiled.match, github_assets.keys()))
- )
- num_matches = len(matches)
-
- # validate artifact pattern matches single asset
- if num_matches == 0:
- self[pattern] = None
- elif num_matches == 1:
- self[pattern] = github_assets[matches[0].group(0)]
- else:
- raise CrossbowError(
- 'Only a single asset should match pattern `{}`, there are '
- 'multiple ones: {}'.format(pattern, ', '.join(matches))
- )
-
- def missing_patterns(self):
- return [pattern for pattern, asset in self.items() if asset is None]
-
- def uploaded_assets(self):
- return [asset for asset in self.values() if asset is not None]
-
-
-class Job(Serializable):
- """Describes multiple tasks against a single target repository"""
-
- def __init__(self, target, tasks, params=None, template_searchpath=None):
- if not tasks:
- raise ValueError('no tasks were provided for the job')
- if not all(isinstance(task, Task) for task in tasks.values()):
- raise ValueError('each `tasks` mus be an instance of Task')
- if not isinstance(target, Target):
- raise ValueError('`target` must be an instance of Target')
- if not isinstance(target, Target):
- raise ValueError('`target` must be an instance of Target')
- if not isinstance(params, dict):
- raise ValueError('`params` must be an instance of dict')
-
- self.target = target
- self.tasks = tasks
- self.params = params or {} # additional parameters for the tasks
- self.branch = None # filled after adding to a queue
- self._queue = None # set by the queue object after put or get
- if template_searchpath is None:
- self._template_searchpath = ArrowSources.find().path
- else:
- self._template_searchpath = template_searchpath
-
- def render_files(self):
- with StringIO() as buf:
- yaml.dump(self, buf)
- content = buf.getvalue()
- tree = {**_default_tree, "job.yml": content}
- return _unflatten_tree(tree)
-
- def render_tasks(self, params=None):
- result = {}
- params = {
- **self.params,
- "arrow": self.target,
- **(params or {})
- }
- for task_name, task in self.tasks.items():
- files = task.render_files(self._template_searchpath, params)
- result[task_name] = files
- return result
-
- @property
- def template_searchpath(self):
- return self._template_searchpath
-
- @property
- def queue(self):
- assert isinstance(self._queue, Queue)
- return self._queue
-
- @queue.setter
- def queue(self, queue):
- assert isinstance(queue, Queue)
- self._queue = queue
- for task in self.tasks.values():
- task._queue = queue
-
- @property
- def email(self):
- return os.environ.get('CROSSBOW_EMAIL', self.target.email)
-
- @property
- def date(self):
- return self.queue.date_of(self)
-
- def show(self, stream=None):
- return yaml.dump(self, stream=stream)
-
- @classmethod
- def from_config(cls, config, target, tasks=None, groups=None, params=None):
- """
- Intantiate a job from based on a config.
-
- Parameters
- ----------
- config : dict
- Deserialized content of tasks.yml
- target : Target
- Describes target repository and revision the builds run against.
- tasks : Optional[List[str]], default None
- List of glob patterns for matching task names.
- groups : Optional[List[str]], default None
- List of exact group names matching predefined task sets in the
- config.
- params : Optional[Dict[str, str]], default None
- Additional rendering parameters for the task templates.
-
- Returns
- -------
- Job
-
- Raises
- ------
- Exception:
- If invalid groups or tasks has been passed.
- """
- task_definitions = config.select(tasks, groups=groups)
-
- # instantiate the tasks
- tasks = {}
- versions = {'version': target.version,
- 'no_rc_version': target.no_rc_version,
- 'no_rc_semver_version': target.no_rc_semver_version}
- for task_name, task in task_definitions.items():
- artifacts = task.pop('artifacts', None) or [] # because of yaml
- artifacts = [fn.format(**versions) for fn in artifacts]
- tasks[task_name] = Task(artifacts=artifacts, **task)
-
- return cls(target=target, tasks=tasks, params=params,
- template_searchpath=config.template_searchpath)
-
- def is_finished(self):
- for task in self.tasks.values():
- status = task.status(force_query=True)
- if status.combined_state == 'pending':
- return False
- return True
-
- def wait_until_finished(self, poll_max_minutes=120,
- poll_interval_minutes=10):
- started_at = time.time()
- while True:
- if self.is_finished():
- break
-
- waited_for_minutes = (time.time() - started_at) / 60
- if waited_for_minutes > poll_max_minutes:
- msg = ('Exceeded the maximum amount of time waiting for job '
- 'to finish, waited for {} minutes.')
- raise RuntimeError(msg.format(waited_for_minutes))
-
- logger.info('Waiting {} minutes and then checking again'
- .format(poll_interval_minutes))
- time.sleep(poll_interval_minutes * 60)
-
-
-class Config(dict):
-
- def __init__(self, tasks, template_searchpath):
- super().__init__(tasks)
- self.template_searchpath = template_searchpath
-
- @classmethod
- def load_yaml(cls, path):
- path = Path(path)
- searchpath = path.parent
- rendered = _render_jinja_template(searchpath, template=path.name,
- params={})
- config = yaml.load(rendered)
- return cls(config, template_searchpath=searchpath)
-
- def show(self, stream=None):
- return yaml.dump(dict(self), stream=stream)
-
- def select(self, tasks=None, groups=None):
- config_groups = dict(self['groups'])
- config_tasks = dict(self['tasks'])
- valid_groups = set(config_groups.keys())
- valid_tasks = set(config_tasks.keys())
- group_whitelist = list(groups or [])
- task_whitelist = list(tasks or [])
-
- # validate that the passed groups are defined in the config
- requested_groups = set(group_whitelist)
- invalid_groups = requested_groups - valid_groups
- if invalid_groups:
- msg = 'Invalid group(s) {!r}. Must be one of {!r}'.format(
- invalid_groups, valid_groups
- )
- raise CrossbowError(msg)
-
- # merge the tasks defined in the selected groups
- task_patterns = [list(config_groups[name]) for name in group_whitelist]
- task_patterns = set(sum(task_patterns, task_whitelist))
-
- # treat the task names as glob patterns to select tasks more easily
- requested_tasks = set()
- for pattern in task_patterns:
- matches = fnmatch.filter(valid_tasks, pattern)
- if len(matches):
- requested_tasks.update(matches)
- else:
- raise CrossbowError(
- "Unable to match any tasks for `{}`".format(pattern)
- )
-
- # validate that the passed and matched tasks are defined in the config
- invalid_tasks = requested_tasks - valid_tasks
- if invalid_tasks:
- msg = 'Invalid task(s) {!r}. Must be one of {!r}'.format(
- invalid_tasks, valid_tasks
- )
- raise CrossbowError(msg)
-
- return {
- task_name: config_tasks[task_name] for task_name in requested_tasks
- }
-
- def validate(self):
- # validate that the task groups are properly referening the tasks
- for group_name, group in self['groups'].items():
- for pattern in group:
- tasks = self.select(tasks=[pattern])
- if not tasks:
- raise CrossbowError(
- "The pattern `{}` defined for task group `{}` is not "
- "matching any of the tasks defined in the "
- "configuration file.".format(pattern, group_name)
- )
-
- # validate that the tasks are constructible
- for task_name, task in self['tasks'].items():
- try:
- Task(**task)
- except Exception as e:
- raise CrossbowError(
- 'Unable to construct a task object from the '
- 'definition of task `{}`. The original error message '
- 'is: `{}`'.format(task_name, str(e))
- )
-
- # validate that the defined tasks are renderable, in order to to that
- # define the required object with dummy data
- target = Target(
- head='e279a7e06e61c14868ca7d71dea795420aea6539',
- branch='master',
- remote='https://github.com/apache/arrow',
- version='1.0.0dev123',
- email='dummy@example.ltd'
- )
-
- for task_name, task in self['tasks'].items():
- task = Task(**task)
- files = task.render_files(
- self.template_searchpath,
- params=dict(
- arrow=target,
- queue_remote_url='https://github.com/org/crossbow'
- )
- )
- if not files:
- raise CrossbowError('No files have been rendered for task `{}`'
- .format(task_name))
-
-
-# configure yaml serializer
-yaml = YAML()
-yaml.register_class(Job)
-yaml.register_class(Task)
-yaml.register_class(Target)
diff --git a/dev/archery/archery/crossbow/reports.py b/dev/archery/archery/crossbow/reports.py
deleted file mode 100644
index bc82db7..0000000
--- a/dev/archery/archery/crossbow/reports.py
+++ /dev/null
@@ -1,302 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-import click
-import collections
-import operator
-import functools
-from io import StringIO
-import textwrap
-
-
-# TODO(kszucs): use archery.report.JinjaReport instead
-class Report:
-
- def __init__(self, job):
- self.job = job
-
- def show(self):
- raise NotImplementedError()
-
-
-class ConsoleReport(Report):
- """Report the status of a Job to the console using click"""
-
- # output table's header template
- HEADER = '[{state:>7}] {branch:<52} {content:>16}'
- DETAILS = ' └ {url}'
-
- # output table's row template for assets
- ARTIFACT_NAME = '{artifact:>69} '
- ARTIFACT_STATE = '[{state:>7}]'
-
- # state color mapping to highlight console output
- COLORS = {
- # from CombinedStatus
- 'error': 'red',
- 'failure': 'red',
- 'pending': 'yellow',
- 'success': 'green',
- # custom state messages
- 'ok': 'green',
- 'missing': 'red'
- }
-
- def lead(self, state, branch, n_uploaded, n_expected):
- line = self.HEADER.format(
- state=state.upper(),
- branch=branch,
- content='uploaded {} / {}'.format(n_uploaded, n_expected)
- )
- return click.style(line, fg=self.COLORS[state.lower()])
-
- def header(self):
- header = self.HEADER.format(
- state='state',
- branch='Task / Branch',
- content='Artifacts'
- )
- delimiter = '-' * len(header)
- return '{}\n{}'.format(header, delimiter)
-
- def artifact(self, state, pattern, asset):
- if asset is None:
- artifact = pattern
- state = 'pending' if state == 'pending' else 'missing'
- else:
- artifact = asset.name
- state = 'ok'
-
- name_ = self.ARTIFACT_NAME.format(artifact=artifact)
- state_ = click.style(
- self.ARTIFACT_STATE.format(state=state.upper()),
- self.COLORS[state]
- )
- return name_ + state_
-
- def show(self, outstream, asset_callback=None):
- echo = functools.partial(click.echo, file=outstream)
-
- # write table's header
- echo(self.header())
-
- # write table's body
- for task_name, task in sorted(self.job.tasks.items()):
- # if not task_name.startswith("test-debian-10-python-3"):
- # continue
- # write summary of the uploaded vs total assets
- status = task.status()
- assets = task.assets()
-
- # mapping of artifact pattern to asset or None of not uploaded
- n_expected = len(task.artifacts)
- n_uploaded = len(assets.uploaded_assets())
- echo(self.lead(status.combined_state, task_name, n_uploaded,
- n_expected))
-
- # show link to the actual build, some of the CI providers implement
- # the statuses API others implement the checks API, so display both
- for link in status.build_links:
- echo(self.DETAILS.format(url=link))
-
- # write per asset status
- for artifact_pattern, asset in assets.items():
- if asset_callback is not None:
- asset_callback(task_name, task, asset)
- echo(self.artifact(status.combined_state, artifact_pattern,
- asset))
-
-
-class EmailReport(Report):
-
- HEADER = textwrap.dedent("""
- Arrow Build Report for Job {job_name}
-
- All tasks: {all_tasks_url}
- """)
-
- TASK = textwrap.dedent("""
- - {name}:
- URL: {url}
- """).strip()
-
- EMAIL = textwrap.dedent("""
- From: {sender_name} <{sender_email}>
- To: {recipient_email}
- Subject: {subject}
-
- {body}
- """).strip()
-
- STATUS_HEADERS = {
- # from CombinedStatus
- 'error': 'Errored Tasks:',
- 'failure': 'Failed Tasks:',
- 'pending': 'Pending Tasks:',
- 'success': 'Succeeded Tasks:',
- }
-
- def __init__(self, job, sender_name, sender_email, recipient_email):
- self.sender_name = sender_name
- self.sender_email = sender_email
- self.recipient_email = recipient_email
- super().__init__(job)
-
- def url(self, query):
- repo_url = self.job.queue.remote_url.strip('.git')
- return '{}/branches/all?query={}'.format(repo_url, query)
-
- def listing(self, tasks):
- return '\n'.join(
- sorted(
- self.TASK.format(name=task_name, url=self.url(task.branch))
- for task_name, task in tasks.items()
- )
- )
-
- def header(self):
- url = self.url(self.job.branch)
- return self.HEADER.format(job_name=self.job.branch, all_tasks_url=url)
-
- def subject(self):
- return (
- "[NIGHTLY] Arrow Build Report for Job {}".format(self.job.branch)
- )
-
- def body(self):
- buffer = StringIO()
- buffer.write(self.header())
-
- tasks_by_state = collections.defaultdict(dict)
- for task_name, task in self.job.tasks.items():
- state = task.status().combined_state
- tasks_by_state[state][task_name] = task
-
- for state in ('failure', 'error', 'pending', 'success'):
- if state in tasks_by_state:
- tasks = tasks_by_state[state]
- buffer.write('\n')
- buffer.write(self.STATUS_HEADERS[state])
- buffer.write('\n')
- buffer.write(self.listing(tasks))
- buffer.write('\n')
-
- return buffer.getvalue()
-
- def email(self):
- return self.EMAIL.format(
- sender_name=self.sender_name,
- sender_email=self.sender_email,
- recipient_email=self.recipient_email,
- subject=self.subject(),
- body=self.body()
- )
-
- def show(self, outstream):
- outstream.write(self.email())
-
- def send(self, smtp_user, smtp_password, smtp_server, smtp_port):
- import smtplib
-
- email = self.email()
-
- server = smtplib.SMTP_SSL(smtp_server, smtp_port)
- server.ehlo()
- server.login(smtp_user, smtp_password)
- server.sendmail(smtp_user, self.recipient_email, email)
- server.close()
-
-
-class CommentReport(Report):
-
- _markdown_badge = '[![{title}]({badge})]({url})'
-
- badges = {
- 'github': _markdown_badge.format(
- title='Github Actions',
- url='https://github.com/{repo}/actions?query=branch:{branch}',
- badge=(
- 'https://github.com/{repo}/workflows/Crossbow/'
- 'badge.svg?branch={branch}'
- ),
- ),
- 'azure': _markdown_badge.format(
- title='Azure',
- url=(
- 'https://dev.azure.com/{repo}/_build/latest'
- '?definitionId=1&branchName={branch}'
- ),
- badge=(
- 'https://dev.azure.com/{repo}/_apis/build/status/'
- '{repo_dotted}?branchName={branch}'
- )
- ),
- 'travis': _markdown_badge.format(
- title='TravisCI',
- url='https://travis-ci.com/{repo}/branches',
- badge='https://img.shields.io/travis/{repo}/{branch}.svg'
- ),
- 'circle': _markdown_badge.format(
- title='CircleCI',
- url='https://circleci.com/gh/{repo}/tree/{branch}',
- badge=(
- 'https://img.shields.io/circleci/build/github'
- '/{repo}/{branch}.svg'
- )
- ),
- 'appveyor': _markdown_badge.format(
- title='Appveyor',
- url='https://ci.appveyor.com/project/{repo}/history',
- badge='https://img.shields.io/appveyor/ci/{repo}/{branch}.svg'
- ),
- 'drone': _markdown_badge.format(
- title='Drone',
- url='https://cloud.drone.io/{repo}',
- badge='https://img.shields.io/drone/build/{repo}/{branch}.svg'
- ),
- }
-
- def __init__(self, job, crossbow_repo):
- self.crossbow_repo = crossbow_repo
- super().__init__(job)
-
- def show(self):
- url = 'https://github.com/{repo}/branches/all?query={branch}'
- sha = self.job.target.head
-
- msg = 'Revision: {}\n\n'.format(sha)
- msg += 'Submitted crossbow builds: [{repo} @ {branch}]'
- msg += '({})\n'.format(url)
- msg += '\n|Task|Status|\n|----|------|'
-
- tasks = sorted(self.job.tasks.items(), key=operator.itemgetter(0))
- for key, task in tasks:
- branch = task.branch
-
- try:
- template = self.badges[task.ci]
- badge = template.format(
- repo=self.crossbow_repo,
- repo_dotted=self.crossbow_repo.replace('/', '.'),
- branch=branch
- )
- except KeyError:
- badge = 'unsupported CI service `{}`'.format(task.ci)
-
- msg += '\n|{}|{}|'.format(key, badge)
-
- return msg.format(repo=self.crossbow_repo, branch=self.job.branch)
diff --git a/dev/archery/archery/crossbow/tests/fixtures/crossbow-job.yaml b/dev/archery/archery/crossbow/tests/fixtures/crossbow-job.yaml
deleted file mode 100644
index c37c7b5..0000000
--- a/dev/archery/archery/crossbow/tests/fixtures/crossbow-job.yaml
+++ /dev/null
@@ -1,51 +0,0 @@
-!Job
-target: !Target
- head: f766a1d615dd1b7ee706d05102e579195951a61c
- email: unkown
- branch: refs/pull/4435/merge
- remote: https://github.com/apache/arrow
- version: 0.13.0.dev306
- no_rc_version: 0.13.0.dev306
-tasks:
- docker-cpp-cmake32: !Task
- ci: circle
- platform: linux
- template: docker-tests/circle.linux.yml
- artifacts: []
- params:
- commands:
- - docker-compose build cpp-cmake32
- - docker-compose run cpp-cmake32
- branch: ursabot-1-circle-docker-cpp-cmake32
- commit: a56b077c8d1b891a7935048e5672bf6fc07599ec
- wheel-osx-cp37m: !Task
- ci: travis
- platform: osx
- template: python-wheels/travis.osx.yml
- artifacts:
- - pyarrow-0.13.0.dev306-cp37-cp37m-macosx_10_6_intel.whl
- params:
- python_version: 3.7
- branch: ursabot-1-travis-wheel-osx-cp37m
- commit: a56b077c8d1b891a7935048e5672bf6fc07599ec
- wheel-osx-cp36m: !Task
- ci: travis
- platform: osx
- template: python-wheels/travis.osx.yml
- artifacts:
- - pyarrow-0.13.0.dev306-cp36-cp36m-macosx_10_6_intel.whl
- params:
- python_version: 3.6
- branch: ursabot-1-travis-wheel-osx-cp36m
- commit: a56b077c8d1b891a7935048e5672bf6fc07599ec
- wheel-win-cp36m: !Task
- ci: appveyor
- platform: win
- template: python-wheels/appveyor.yml
- artifacts:
- - pyarrow-0.13.0.dev306-cp36-cp36m-win_amd64.whl
- params:
- python_version: 3.6
- branch: ursabot-1-appveyor-wheel-win-cp36m
- commit: a56b077c8d1b891a7935048e5672bf6fc07599ec
-branch: ursabot-1
diff --git a/dev/archery/archery/crossbow/tests/fixtures/crossbow-success-message.md b/dev/archery/archery/crossbow/tests/fixtures/crossbow-success-message.md
deleted file mode 100644
index f914287..0000000
--- a/dev/archery/archery/crossbow/tests/fixtures/crossbow-success-message.md
+++ /dev/null
@@ -1,10 +0,0 @@
-Revision: {revision}
-
-Submitted crossbow builds: [{repo} @ {branch}](https://github.com/{repo}/branches/all?query={branch})
-
-| Task | Status |
-| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| docker-cpp-cmake32 | [![CircleCI](https://img.shields.io/circleci/build/github/{repo}/{branch}-circle-docker-cpp-cmake32.svg)](https://circleci.com/gh/{repo}/tree/{branch}-circle-docker-cpp-cmake32) |
-| wheel-osx-cp36m | [![TravisCI](https://img.shields.io/travis/{repo}/{branch}-travis-wheel-osx-cp36m.svg)](https://travis-ci.com/{repo}/branches) |
-| wheel-osx-cp37m | [![TravisCI](https://img.shields.io/travis/{repo}/{branch}-travis-wheel-osx-cp37m.svg)](https://travis-ci.com/{repo}/branches) |
-| wheel-win-cp36m | [![Appveyor](https://img.shields.io/appveyor/ci/{repo}/{branch}-appveyor-wheel-win-cp36m.svg)](https://ci.appveyor.com/project/{repo}/history) |
diff --git a/dev/archery/archery/crossbow/tests/test_core.py b/dev/archery/archery/crossbow/tests/test_core.py
deleted file mode 100644
index 5184742..0000000
--- a/dev/archery/archery/crossbow/tests/test_core.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-from archery.utils.source import ArrowSources
-from archery.crossbow import Config
-
-
-def test_config():
- src = ArrowSources.find()
- conf = Config.load_yaml(src.dev / "tasks" / "tasks.yml")
- conf.validate()
diff --git a/dev/archery/archery/crossbow/tests/test_crossbow_cli.py b/dev/archery/archery/crossbow/tests/test_crossbow_cli.py
deleted file mode 100644
index ee9ba1e..0000000
--- a/dev/archery/archery/crossbow/tests/test_crossbow_cli.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-from click.testing import CliRunner
-import pytest
-
-from archery.crossbow.cli import crossbow
-from archery.utils.git import git
-
-
-@pytest.mark.integration
-def test_crossbow_submit(tmp_path):
- runner = CliRunner()
-
- def invoke(*args):
- return runner.invoke(crossbow, ['--queue-path', str(tmp_path), *args])
-
- # initialize an empty crossbow repository
- git.run_cmd("init", str(tmp_path))
- git.run_cmd("-C", str(tmp_path), "remote", "add", "origin",
- "https://github.com/dummy/repo")
- git.run_cmd("-C", str(tmp_path), "commit", "-m", "initial",
- "--allow-empty")
-
- result = invoke('check-config')
- assert result.exit_code == 0
-
- result = invoke('submit', '--no-fetch', '--no-push', '-g', 'wheel')
- assert result.exit_code == 0
diff --git a/dev/archery/archery/crossbow/tests/test_reports.py b/dev/archery/archery/crossbow/tests/test_reports.py
deleted file mode 100644
index 0df292b..0000000
--- a/dev/archery/archery/crossbow/tests/test_reports.py
+++ /dev/null
@@ -1,35 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-import textwrap
-
-from archery.crossbow.core import yaml
-from archery.crossbow.reports import CommentReport
-
-
-def test_crossbow_comment_formatter(load_fixture):
- msg = load_fixture('crossbow-success-message.md')
- job = load_fixture('crossbow-job.yaml', decoder=yaml.load)
-
- report = CommentReport(job, crossbow_repo='ursa-labs/crossbow')
- expected = msg.format(
- repo='ursa-labs/crossbow',
- branch='ursabot-1',
- revision='f766a1d615dd1b7ee706d05102e579195951a61c',
- status='has been succeeded.'
- )
- assert report.show() == textwrap.dedent(expected).strip()
diff --git a/dev/archery/archery/docker.py b/dev/archery/archery/docker.py
deleted file mode 100644
index 17d4c71..0000000
--- a/dev/archery/archery/docker.py
+++ /dev/null
@@ -1,402 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-import os
-import re
-import subprocess
-from io import StringIO
-
-from dotenv import dotenv_values
-from ruamel.yaml import YAML
-
-from .utils.command import Command, default_bin
-from .compat import _ensure_path
-
-
-def flatten(node, parents=None):
- parents = list(parents or [])
- if isinstance(node, str):
- yield (node, parents)
- elif isinstance(node, list):
- for value in node:
- yield from flatten(value, parents=parents)
- elif isinstance(node, dict):
- for key, value in node.items():
- yield (key, parents)
- yield from flatten(value, parents=parents + [key])
- else:
- raise TypeError(node)
-
-
-def _sanitize_command(cmd):
- if isinstance(cmd, list):
- cmd = " ".join(cmd)
- return re.sub(r"\s+", " ", cmd)
-
-
-class UndefinedImage(Exception):
- pass
-
-
-class ComposeConfig:
-
- def __init__(self, config_path, dotenv_path, compose_bin, params=None):
- config_path = _ensure_path(config_path)
- if dotenv_path:
- dotenv_path = _ensure_path(dotenv_path)
- else:
- dotenv_path = config_path.parent / '.env'
- self._read_env(dotenv_path, params)
- self._read_config(config_path, compose_bin)
-
- def _read_env(self, dotenv_path, params):
- """
- Read .env and merge it with explicitly passed parameters.
- """
- self.dotenv = dotenv_values(str(dotenv_path))
- if params is None:
- self.params = {}
- else:
- self.params = {k: v for k, v in params.items() if k in self.dotenv}
-
- # forward the process' environment variables
- self.env = os.environ.copy()
- # set the defaults from the dotenv files
- self.env.update(self.dotenv)
- # override the defaults passed as parameters
- self.env.update(self.params)
-
- # translate docker's architecture notation to a more widely used one
- arch = self.env.get('ARCH', 'amd64')
- arch_aliases = {
- 'amd64': 'x86_64',
- 'arm64v8': 'aarch64',
- 's390x': 's390x'
- }
- arch_short_aliases = {
- 'amd64': 'x64',
- 'arm64v8': 'arm64',
- 's390x': 's390x'
- }
- self.env['ARCH_ALIAS'] = arch_aliases.get(arch, arch)
- self.env['ARCH_SHORT_ALIAS'] = arch_short_aliases.get(arch, arch)
-
- def _read_config(self, config_path, compose_bin):
- """
- Validate and read the docker-compose.yml
- """
- yaml = YAML()
- with config_path.open() as fp:
- config = yaml.load(fp)
-
- services = config['services'].keys()
- self.hierarchy = dict(flatten(config.get('x-hierarchy', {})))
- self.with_gpus = config.get('x-with-gpus', [])
- nodes = self.hierarchy.keys()
- errors = []
-
- for name in self.with_gpus:
- if name not in services:
- errors.append(
- 'Service `{}` defined in `x-with-gpus` bot not in '
- '`services`'.format(name)
- )
- for name in nodes - services:
- errors.append(
- 'Service `{}` is defined in `x-hierarchy` bot not in '
- '`services`'.format(name)
- )
- for name in services - nodes:
- errors.append(
- 'Service `{}` is defined in `services` but not in '
- '`x-hierarchy`'.format(name)
- )
-
- # trigger docker-compose's own validation
- compose = Command('docker-compose')
- args = ['--file', str(config_path), 'config']
- result = compose.run(*args, env=self.env, check=False,
- stderr=subprocess.PIPE, stdout=subprocess.PIPE)
-
- if result.returncode != 0:
- # strip the intro line of docker-compose errors
- errors += result.stderr.decode().splitlines()
-
- if errors:
- msg = '\n'.join([' - {}'.format(msg) for msg in errors])
- raise ValueError(
- 'Found errors with docker-compose:\n{}'.format(msg)
- )
-
- rendered_config = StringIO(result.stdout.decode())
- self.path = config_path
- self.config = yaml.load(rendered_config)
-
- def get(self, service_name):
- try:
- service = self.config['services'][service_name]
- except KeyError:
- raise UndefinedImage(service_name)
- service['name'] = service_name
- service['need_gpu'] = service_name in self.with_gpus
- service['ancestors'] = self.hierarchy[service_name]
- return service
-
- def __getitem__(self, service_name):
- return self.get(service_name)
-
-
-class Docker(Command):
-
- def __init__(self, docker_bin=None):
- self.bin = default_bin(docker_bin, "docker")
-
-
-class DockerCompose(Command):
-
- def __init__(self, config_path, dotenv_path=None, compose_bin=None,
- params=None):
- compose_bin = default_bin(compose_bin, 'docker-compose')
- self.config = ComposeConfig(config_path, dotenv_path, compose_bin,
- params)
- self.bin = compose_bin
- self.pull_memory = set()
-
- def clear_pull_memory(self):
- self.pull_memory = set()
-
- def _execute_compose(self, *args, **kwargs):
- # execute as a docker compose command
- try:
- result = super().run('--file', str(self.config.path), *args,
- env=self.config.env, **kwargs)
- result.check_returncode()
- except subprocess.CalledProcessError as e:
- def formatdict(d, template):
- return '\n'.join(
- template.format(k, v) for k, v in sorted(d.items())
- )
- msg = (
- "`{cmd}` exited with a non-zero exit code {code}, see the "
- "process log above.\n\nThe docker-compose command was "
- "invoked with the following parameters:\n\nDefaults defined "
- "in .env:\n{dotenv}\n\nArchery was called with:\n{params}"
- )
- raise RuntimeError(
- msg.format(
- cmd=' '.join(e.cmd),
- code=e.returncode,
- dotenv=formatdict(self.config.dotenv, template=' {}: {}'),
- params=formatdict(
- self.config.params, template=' export {}={}'
- )
- )
- )
-
- def _execute_docker(self, *args, **kwargs):
- # execute as a plain docker cli command
- try:
- result = Docker().run(*args, **kwargs)
- result.check_returncode()
- except subprocess.CalledProcessError as e:
- raise RuntimeError(
- "{} exited with non-zero exit code {}".format(
- ' '.join(e.cmd), e.returncode
- )
- )
-
- def pull(self, service_name, pull_leaf=True, using_docker=False):
- def _pull(service):
- args = ['pull']
- if service['image'] in self.pull_memory:
- return
-
- if using_docker:
- try:
- self._execute_docker(*args, service['image'])
- except Exception as e:
- # better --ignore-pull-failures handling
- print(e)
- else:
- args.append('--ignore-pull-failures')
- self._execute_compose(*args, service['name'])
-
- self.pull_memory.add(service['image'])
-
- service = self.config.get(service_name)
- for ancestor in service['ancestors']:
- _pull(self.config.get(ancestor))
- if pull_leaf:
- _pull(service)
-
- def build(self, service_name, use_cache=True, use_leaf_cache=True,
- using_docker=False, using_buildx=False):
- def _build(service, use_cache):
- if 'build' not in service:
- # nothing to do
- return
-
- args = []
- cache_from = list(service.get('build', {}).get('cache_from', []))
- if use_cache:
- for image in cache_from:
- if image not in self.pull_memory:
- try:
- self._execute_docker('pull', image)
- except Exception as e:
- print(e)
- finally:
- self.pull_memory.add(image)
- else:
- args.append('--no-cache')
-
- # turn on inline build cache, this is a docker buildx feature
- # used to bundle the image build cache to the pushed image manifest
- # so the build cache can be reused across hosts, documented at
- # https://github.com/docker/buildx#--cache-tonametypetypekeyvalue
- if self.config.env.get('BUILDKIT_INLINE_CACHE') == '1':
- args.extend(['--build-arg', 'BUILDKIT_INLINE_CACHE=1'])
-
- if using_buildx:
- for k, v in service['build'].get('args', {}).items():
- args.extend(['--build-arg', '{}={}'.format(k, v)])
-
- if use_cache:
- cache_ref = '{}-cache'.format(service['image'])
- cache_from = 'type=registry,ref={}'.format(cache_ref)
- cache_to = (
- 'type=registry,ref={},mode=max'.format(cache_ref)
- )
- args.extend([
- '--cache-from', cache_from,
- '--cache-to', cache_to,
- ])
-
- args.extend([
- '--output', 'type=docker',
- '-f', service['build']['dockerfile'],
- '-t', service['image'],
- service['build'].get('context', '.')
- ])
- self._execute_docker("buildx", "build", *args)
- elif using_docker:
- # better for caching
- for k, v in service['build'].get('args', {}).items():
- args.extend(['--build-arg', '{}={}'.format(k, v)])
- for img in cache_from:
- args.append('--cache-from="{}"'.format(img))
- args.extend([
- '-f', service['build']['dockerfile'],
- '-t', service['image'],
- service['build'].get('context', '.')
- ])
- self._execute_docker("build", *args)
- else:
- self._execute_compose("build", *args, service['name'])
-
- service = self.config.get(service_name)
- # build ancestor services
- for ancestor in service['ancestors']:
- _build(self.config.get(ancestor), use_cache=use_cache)
- # build the leaf/target service
- _build(service, use_cache=use_cache and use_leaf_cache)
-
- def run(self, service_name, command=None, *, env=None, volumes=None,
- user=None, using_docker=False):
- service = self.config.get(service_name)
-
- args = []
- if user is not None:
- args.extend(['-u', user])
-
- if env is not None:
- for k, v in env.items():
- args.extend(['-e', '{}={}'.format(k, v)])
-
- if volumes is not None:
- for volume in volumes:
- args.extend(['--volume', volume])
-
- if using_docker or service['need_gpu']:
- # use gpus, requires docker>=19.03
- if service['need_gpu']:
- args.extend(['--gpus', 'all'])
-
- if service.get('shm_size'):
- args.extend(['--shm-size', service['shm_size']])
-
- # append env variables from the compose conf
- for k, v in service.get('environment', {}).items():
- args.extend(['-e', '{}={}'.format(k, v)])
-
- # append volumes from the compose conf
- for v in service.get('volumes', []):
- if not isinstance(v, str):
- # if not the compact string volume definition
- v = "{}:{}".format(v['source'], v['target'])
- args.extend(['-v', v])
-
- # infer whether an interactive shell is desired or not
- if command in ['cmd.exe', 'bash', 'sh', 'powershell']:
- args.append('-it')
-
- # get the actual docker image name instead of the compose service
- # name which we refer as image in general
- args.append(service['image'])
-
- # add command from compose if it wasn't overridden
- if command is not None:
- args.append(command)
- else:
- # replace whitespaces from the preformatted compose command
- cmd = _sanitize_command(service.get('command', ''))
- if cmd:
- args.append(cmd)
-
- # execute as a plain docker cli command
- self._execute_docker('run', '--rm', *args)
- else:
- # execute as a docker-compose command
- args.append(service_name)
- if command is not None:
- args.append(command)
- self._execute_compose('run', '--rm', *args)
-
- def push(self, service_name, user=None, password=None, using_docker=False):
- def _push(service):
- if using_docker:
- return self._execute_docker('push', service['image'])
- else:
- return self._execute_compose('push', service['name'])
-
- if user is not None:
- try:
- # TODO(kszucs): have an option for a prompt
- self._execute_docker('login', '-u', user, '-p', password)
- except subprocess.CalledProcessError:
- # hide credentials
- msg = ('Failed to push `{}`, check the passed credentials'
- .format(service_name))
- raise RuntimeError(msg) from None
-
- service = self.config.get(service_name)
- for ancestor in service['ancestors']:
- _push(self.config.get(ancestor))
- _push(service)
-
- def images(self):
- return sorted(self.config.hierarchy.keys())
diff --git a/dev/archery/archery/integration/__init__.py b/dev/archery/archery/integration/__init__.py
deleted file mode 100644
index 13a8339..0000000
--- a/dev/archery/archery/integration/__init__.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
diff --git a/dev/archery/archery/integration/datagen.py b/dev/archery/archery/integration/datagen.py
deleted file mode 100644
index 35ab289..0000000
--- a/dev/archery/archery/integration/datagen.py
+++ /dev/null
@@ -1,1604 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-from collections import namedtuple, OrderedDict
-import binascii
-import json
-import os
-import random
-import tempfile
-
-import numpy as np
-
-from .util import frombytes, tobytes, random_bytes, random_utf8
-
-
-def metadata_key_values(pairs):
- return [{'key': k, 'value': v} for k, v in pairs]
-
-
-class Field(object):
-
- def __init__(self, name, *, nullable=True, metadata=None):
- self.name = name
- self.nullable = nullable
- self.metadata = metadata or []
-
- def get_json(self):
- entries = [
- ('name', self.name),
- ('type', self._get_type()),
- ('nullable', self.nullable),
- ('children', self._get_children()),
- ]
-
- dct = self._get_dictionary()
- if dct:
- entries.append(('dictionary', dct))
-
- if self.metadata is not None and len(self.metadata) > 0:
- entries.append(('metadata', metadata_key_values(self.metadata)))
-
- return OrderedDict(entries)
-
- def _get_dictionary(self):
- return None
-
- def _make_is_valid(self, size, null_probability=0.4):
- if self.nullable:
- return (np.random.random_sample(size) > null_probability
- ).astype(np.int8)
- else:
- return np.ones(size, dtype=np.int8)
-
-
-class Column(object):
-
- def __init__(self, name, count):
- self.name = name
- self.count = count
-
- def __len__(self):
- return self.count
-
- def _get_children(self):
- return []
-
- def _get_buffers(self):
- return []
-
- def get_json(self):
- entries = [
- ('name', self.name),
- ('count', self.count)
- ]
-
- buffers = self._get_buffers()
- entries.extend(buffers)
-
- children = self._get_children()
- if len(children) > 0:
- entries.append(('children', children))
-
- return OrderedDict(entries)
-
-
-class PrimitiveField(Field):
-
- def _get_children(self):
- return []
-
-
-class PrimitiveColumn(Column):
-
- def __init__(self, name, count, is_valid, values):
- super().__init__(name, count)
- self.is_valid = is_valid
- self.values = values
-
- def _encode_value(self, x):
- return x
-
- def _get_buffers(self):
- return [
- ('VALIDITY', [int(v) for v in self.is_valid]),
- ('DATA', list([self._encode_value(x) for x in self.values]))
- ]
-
-
-class NullColumn(Column):
- # This subclass is for readability only
- pass
-
-
-class NullField(PrimitiveField):
-
- def __init__(self, name, metadata=None):
- super().__init__(name, nullable=True,
- metadata=metadata)
-
- def _get_type(self):
- return OrderedDict([('name', 'null')])
-
- def generate_column(self, size, name=None):
- return NullColumn(name or self.name, size)
-
-
-TEST_INT_MAX = 2 ** 31 - 1
-TEST_INT_MIN = ~TEST_INT_MAX
-
-
-class IntegerField(PrimitiveField):
-
- def __init__(self, name, is_signed, bit_width, *, nullable=True,
- metadata=None,
- min_value=TEST_INT_MIN,
- max_value=TEST_INT_MAX):
- super().__init__(name, nullable=nullable,
- metadata=metadata)
- self.is_signed = is_signed
- self.bit_width = bit_width
- self.min_value = min_value
- self.max_value = max_value
-
- def _get_generated_data_bounds(self):
- if self.is_signed:
- signed_iinfo = np.iinfo('int' + str(self.bit_width))
- min_value, max_value = signed_iinfo.min, signed_iinfo.max
- else:
- unsigned_iinfo = np.iinfo('uint' + str(self.bit_width))
- min_value, max_value = 0, unsigned_iinfo.max
-
- lower_bound = max(min_value, self.min_value)
- upper_bound = min(max_value, self.max_value)
- return lower_bound, upper_bound
-
- def _get_type(self):
- return OrderedDict([
- ('name', 'int'),
- ('isSigned', self.is_signed),
- ('bitWidth', self.bit_width)
- ])
-
- def generate_column(self, size, name=None):
- lower_bound, upper_bound = self._get_generated_data_bounds()
- return self.generate_range(size, lower_bound, upper_bound,
- name=name, include_extremes=True)
-
- def generate_range(self, size, lower, upper, name=None,
- include_extremes=False):
- values = np.random.randint(lower, upper, size=size, dtype=np.int64)
- if include_extremes and size >= 2:
- values[:2] = [lower, upper]
- values = list(map(int if self.bit_width < 64 else str, values))
-
- is_valid = self._make_is_valid(size)
-
- if name is None:
- name = self.name
- return PrimitiveColumn(name, size, is_valid, values)
-
-
-class DateField(IntegerField):
-
- DAY = 0
- MILLISECOND = 1
-
- # 1/1/1 to 12/31/9999
- _ranges = {
- DAY: [-719162, 2932896],
- MILLISECOND: [-62135596800000, 253402214400000]
- }
-
- def __init__(self, name, unit, *, nullable=True, metadata=None):
- bit_width = 32 if unit == self.DAY else 64
-
- min_value, max_value = self._ranges[unit]
- super().__init__(
- name, True, bit_width,
- nullable=nullable, metadata=metadata,
- min_value=min_value, max_value=max_value
- )
- self.unit = unit
-
- def _get_type(self):
- return OrderedDict([
- ('name', 'date'),
- ('unit', 'DAY' if self.unit == self.DAY else 'MILLISECOND')
- ])
-
-
-TIMEUNIT_NAMES = {
- 's': 'SECOND',
- 'ms': 'MILLISECOND',
- 'us': 'MICROSECOND',
- 'ns': 'NANOSECOND'
-}
-
-
-class TimeField(IntegerField):
-
- BIT_WIDTHS = {
- 's': 32,
- 'ms': 32,
- 'us': 64,
- 'ns': 64
- }
-
- _ranges = {
- 's': [0, 86400],
- 'ms': [0, 86400000],
- 'us': [0, 86400000000],
- 'ns': [0, 86400000000000]
- }
-
- def __init__(self, name, unit='s', *, nullable=True,
- metadata=None):
- min_val, max_val = self._ranges[unit]
- super().__init__(name, True, self.BIT_WIDTHS[unit],
- nullable=nullable, metadata=metadata,
- min_value=min_val, max_value=max_val)
- self.unit = unit
-
- def _get_type(self):
- return OrderedDict([
- ('name', 'time'),
- ('unit', TIMEUNIT_NAMES[self.unit]),
- ('bitWidth', self.bit_width)
- ])
-
-
-class TimestampField(IntegerField):
-
- # 1/1/1 to 12/31/9999
- _ranges = {
- 's': [-62135596800, 253402214400],
- 'ms': [-62135596800000, 253402214400000],
- 'us': [-62135596800000000, 253402214400000000],
-
- # Physical range for int64, ~584 years and change
- 'ns': [np.iinfo('int64').min, np.iinfo('int64').max]
- }
-
- def __init__(self, name, unit='s', tz=None, *, nullable=True,
- metadata=None):
- min_val, max_val = self._ranges[unit]
- super().__init__(name, True, 64,
- nullable=nullable,
- metadata=metadata,
- min_value=min_val,
- max_value=max_val)
- self.unit = unit
- self.tz = tz
-
- def _get_type(self):
- fields = [
- ('name', 'timestamp'),
- ('unit', TIMEUNIT_NAMES[self.unit])
- ]
-
- if self.tz is not None:
- fields.append(('timezone', self.tz))
-
- return OrderedDict(fields)
-
-
-class DurationIntervalField(IntegerField):
-
- def __init__(self, name, unit='s', *, nullable=True,
- metadata=None):
- min_val, max_val = np.iinfo('int64').min, np.iinfo('int64').max,
- super().__init__(
- name, True, 64,
- nullable=nullable, metadata=metadata,
- min_value=min_val, max_value=max_val)
- self.unit = unit
-
- def _get_type(self):
- fields = [
- ('name', 'duration'),
- ('unit', TIMEUNIT_NAMES[self.unit])
- ]
-
- return OrderedDict(fields)
-
-
-class YearMonthIntervalField(IntegerField):
- def __init__(self, name, *, nullable=True, metadata=None):
- min_val, max_val = [-10000*12, 10000*12] # +/- 10000 years.
- super().__init__(
- name, True, 32,
- nullable=nullable, metadata=metadata,
- min_value=min_val, max_value=max_val)
-
- def _get_type(self):
- fields = [
- ('name', 'interval'),
- ('unit', 'YEAR_MONTH'),
- ]
-
- return OrderedDict(fields)
-
-
-class DayTimeIntervalField(PrimitiveField):
- def __init__(self, name, *, nullable=True, metadata=None):
- super().__init__(name,
- nullable=True,
- metadata=metadata)
-
- @property
- def numpy_type(self):
- return object
-
- def _get_type(self):
-
- return OrderedDict([
- ('name', 'interval'),
- ('unit', 'DAY_TIME'),
- ])
-
- def generate_column(self, size, name=None):
- min_day_value, max_day_value = -10000*366, 10000*366
- values = [{'days': random.randint(min_day_value, max_day_value),
- 'milliseconds': random.randint(-86400000, +86400000)}
- for _ in range(size)]
-
- is_valid = self._make_is_valid(size)
- if name is None:
- name = self.name
- return PrimitiveColumn(name, size, is_valid, values)
-
-
-class FloatingPointField(PrimitiveField):
-
- def __init__(self, name, bit_width, *, nullable=True,
- metadata=None):
- super().__init__(name,
- nullable=nullable,
- metadata=metadata)
-
- self.bit_width = bit_width
- self.precision = {
- 16: 'HALF',
- 32: 'SINGLE',
- 64: 'DOUBLE'
- }[self.bit_width]
-
- @property
- def numpy_type(self):
- return 'float' + str(self.bit_width)
-
- def _get_type(self):
- return OrderedDict([
- ('name', 'floatingpoint'),
- ('precision', self.precision)
- ])
-
- def generate_column(self, size, name=None):
- values = np.random.randn(size) * 1000
- values = np.round(values, 3)
-
- is_valid = self._make_is_valid(size)
- if name is None:
- name = self.name
- return PrimitiveColumn(name, size, is_valid, values)
-
-
-DECIMAL_PRECISION_TO_VALUE = {
- key: (1 << (8 * i - 1)) - 1 for i, key in enumerate(
- [1, 3, 5, 7, 10, 12, 15, 17, 19, 22, 24, 27, 29, 32, 34, 36,
- 40, 42, 44, 50, 60, 70],
- start=1,
- )
-}
-
-
-def decimal_range_from_precision(precision):
- assert 1 <= precision <= 76
- try:
- max_value = DECIMAL_PRECISION_TO_VALUE[precision]
- except KeyError:
- return decimal_range_from_precision(precision - 1)
- else:
- return ~max_value, max_value
-
-
-class DecimalField(PrimitiveField):
- def __init__(self, name, precision, scale, bit_width, *,
- nullable=True, metadata=None):
- super().__init__(name, nullable=True,
- metadata=metadata)
- self.precision = precision
- self.scale = scale
- self.bit_width = bit_width
-
- @property
- def numpy_type(self):
- return object
-
- def _get_type(self):
- return OrderedDict([
- ('name', 'decimal'),
- ('precision', self.precision),
- ('scale', self.scale),
- ('bitWidth', self.bit_width),
- ])
-
- def generate_column(self, size, name=None):
- min_value, max_value = decimal_range_from_precision(self.precision)
- values = [random.randint(min_value, max_value) for _ in range(size)]
-
- is_valid = self._make_is_valid(size)
- if name is None:
- name = self.name
- return DecimalColumn(name, size, is_valid, values, self.bit_width)
-
-
-class DecimalColumn(PrimitiveColumn):
-
- def __init__(self, name, count, is_valid, values, bit_width):
- super().__init__(name, count, is_valid, values)
- self.bit_width = bit_width
-
- def _encode_value(self, x):
- return str(x)
-
-
-class BooleanField(PrimitiveField):
- bit_width = 1
-
- def _get_type(self):
- return OrderedDict([('name', 'bool')])
-
- @property
- def numpy_type(self):
- return 'bool'
-
- def generate_column(self, size, name=None):
- values = list(map(bool, np.random.randint(0, 2, size=size)))
- is_valid = self._make_is_valid(size)
- if name is None:
- name = self.name
- return PrimitiveColumn(name, size, is_valid, values)
-
-
-class FixedSizeBinaryField(PrimitiveField):
-
- def __init__(self, name, byte_width, *, nullable=True,
- metadata=None):
- super().__init__(name, nullable=nullable,
- metadata=metadata)
- self.byte_width = byte_width
-
- @property
- def numpy_type(self):
- return object
-
- @property
- def column_class(self):
- return FixedSizeBinaryColumn
-
- def _get_type(self):
- return OrderedDict([('name', 'fixedsizebinary'),
- ('byteWidth', self.byte_width)])
-
- def generate_column(self, size, name=None):
- is_valid = self._make_is_valid(size)
- values = []
-
- for i in range(size):
- values.append(random_bytes(self.byte_width))
-
- if name is None:
- name = self.name
- return self.column_class(name, size, is_valid, values)
-
-
-class BinaryField(PrimitiveField):
-
- @property
- def numpy_type(self):
- return object
-
- @property
- def column_class(self):
- return BinaryColumn
-
- def _get_type(self):
- return OrderedDict([('name', 'binary')])
-
- def _random_sizes(self, size):
- return np.random.exponential(scale=4, size=size).astype(np.int32)
-
- def generate_column(self, size, name=None):
- is_valid = self._make_is_valid(size)
- values = []
-
- sizes = self._random_sizes(size)
-
- for i, nbytes in enumerate(sizes):
- if is_valid[i]:
- values.append(random_bytes(nbytes))
- else:
- values.append(b"")
-
- if name is None:
- name = self.name
- return self.column_class(name, size, is_valid, values)
-
-
-class StringField(BinaryField):
-
- @property
- def column_class(self):
- return StringColumn
-
- def _get_type(self):
- return OrderedDict([('name', 'utf8')])
-
- def generate_column(self, size, name=None):
- K = 7
- is_valid = self._make_is_valid(size)
- values = []
-
- for i in range(size):
- if is_valid[i]:
- values.append(tobytes(random_utf8(K)))
- else:
- values.append(b"")
-
- if name is None:
- name = self.name
- return self.column_class(name, size, is_valid, values)
-
-
-class LargeBinaryField(BinaryField):
-
- @property
- def column_class(self):
- return LargeBinaryColumn
-
- def _get_type(self):
- return OrderedDict([('name', 'largebinary')])
-
-
-class LargeStringField(StringField):
-
- @property
- def column_class(self):
- return LargeStringColumn
-
- def _get_type(self):
- return OrderedDict([('name', 'largeutf8')])
-
-
-class Schema(object):
-
- def __init__(self, fields, metadata=None):
- self.fields = fields
- self.metadata = metadata
-
- def get_json(self):
- entries = [
- ('fields', [field.get_json() for field in self.fields])
- ]
-
- if self.metadata is not None and len(self.metadata) > 0:
- entries.append(('metadata', metadata_key_values(self.metadata)))
-
- return OrderedDict(entries)
-
-
-class _NarrowOffsetsMixin:
-
- def _encode_offsets(self, offsets):
- return list(map(int, offsets))
-
-
-class _LargeOffsetsMixin:
-
- def _encode_offsets(self, offsets):
- # 64-bit offsets have to be represented as strings to roundtrip
- # through JSON.
- return list(map(str, offsets))
-
-
-class _BaseBinaryColumn(PrimitiveColumn):
-
- def _encode_value(self, x):
- return frombytes(binascii.hexlify(x).upper())
-
- def _get_buffers(self):
- offset = 0
- offsets = [0]
-
- data = []
- for i, v in enumerate(self.values):
- if self.is_valid[i]:
- offset += len(v)
- else:
- v = b""
-
- offsets.append(offset)
- data.append(self._encode_value(v))
-
- return [
- ('VALIDITY', [int(x) for x in self.is_valid]),
- ('OFFSET', self._encode_offsets(offsets)),
- ('DATA', data)
- ]
-
-
-class _BaseStringColumn(_BaseBinaryColumn):
-
- def _encode_value(self, x):
- return frombytes(x)
-
-
-class BinaryColumn(_BaseBinaryColumn, _NarrowOffsetsMixin):
- pass
-
-
-class StringColumn(_BaseStringColumn, _NarrowOffsetsMixin):
- pass
-
-
-class LargeBinaryColumn(_BaseBinaryColumn, _LargeOffsetsMixin):
- pass
-
-
-class LargeStringColumn(_BaseStringColumn, _LargeOffsetsMixin):
- pass
-
-
-class FixedSizeBinaryColumn(PrimitiveColumn):
-
- def _encode_value(self, x):
- return frombytes(binascii.hexlify(x).upper())
-
- def _get_buffers(self):
- data = []
- for i, v in enumerate(self.values):
- data.append(self._encode_value(v))
-
- return [
- ('VALIDITY', [int(x) for x in self.is_valid]),
- ('DATA', data)
- ]
-
-
-class ListField(Field):
-
- def __init__(self, name, value_field, *, nullable=True,
- metadata=None):
- super().__init__(name, nullable=nullable,
- metadata=metadata)
- self.value_field = value_field
-
- @property
- def column_class(self):
- return ListColumn
-
- def _get_type(self):
- return OrderedDict([
- ('name', 'list')
- ])
-
- def _get_children(self):
- return [self.value_field.get_json()]
-
- def generate_column(self, size, name=None):
- MAX_LIST_SIZE = 4
-
- is_valid = self._make_is_valid(size)
- list_sizes = np.random.randint(0, MAX_LIST_SIZE + 1, size=size)
- offsets = [0]
-
- offset = 0
- for i in range(size):
- if is_valid[i]:
- offset += int(list_sizes[i])
- offsets.append(offset)
-
- # The offset now is the total number of elements in the child array
- values = self.value_field.generate_column(offset)
-
- if name is None:
- name = self.name
- return self.column_class(name, size, is_valid, offsets, values)
-
-
-class LargeListField(ListField):
-
- @property
- def column_class(self):
- return LargeListColumn
-
- def _get_type(self):
- return OrderedDict([
- ('name', 'largelist')
- ])
-
-
-class _BaseListColumn(Column):
-
- def __init__(self, name, count, is_valid, offsets, values):
- super().__init__(name, count)
- self.is_valid = is_valid
- self.offsets = offsets
- self.values = values
-
- def _get_buffers(self):
- return [
- ('VALIDITY', [int(v) for v in self.is_valid]),
- ('OFFSET', self._encode_offsets(self.offsets))
- ]
-
- def _get_children(self):
- return [self.values.get_json()]
-
-
-class ListColumn(_BaseListColumn, _NarrowOffsetsMixin):
- pass
-
-
-class LargeListColumn(_BaseListColumn, _LargeOffsetsMixin):
- pass
-
-
-class MapField(Field):
-
- def __init__(self, name, key_field, item_field, *, nullable=True,
- metadata=None, keys_sorted=False, entries_name='entries'):
- super().__init__(name, nullable=nullable,
- metadata=metadata)
-
- assert not key_field.nullable
- self.key_field = key_field
- self.item_field = item_field
- self.pair_field = StructField(entries_name, [key_field, item_field],
- nullable=False)
- self.keys_sorted = keys_sorted
-
- def _get_type(self):
- return OrderedDict([
- ('name', 'map'),
- ('keysSorted', self.keys_sorted)
- ])
-
- def _get_children(self):
- return [self.pair_field.get_json()]
-
- def generate_column(self, size, name=None):
- MAX_MAP_SIZE = 4
-
- is_valid = self._make_is_valid(size)
- map_sizes = np.random.randint(0, MAX_MAP_SIZE + 1, size=size)
- offsets = [0]
-
- offset = 0
- for i in range(size):
- if is_valid[i]:
- offset += int(map_sizes[i])
- offsets.append(offset)
-
- # The offset now is the total number of elements in the child array
- pairs = self.pair_field.generate_column(offset)
- if name is None:
- name = self.name
-
- return MapColumn(name, size, is_valid, offsets, pairs)
-
-
-class MapColumn(Column):
-
- def __init__(self, name, count, is_valid, offsets, pairs):
- super().__init__(name, count)
- self.is_valid = is_valid
- self.offsets = offsets
- self.pairs = pairs
-
- def _get_buffers(self):
- return [
- ('VALIDITY', [int(v) for v in self.is_valid]),
- ('OFFSET', list(self.offsets))
- ]
-
- def _get_children(self):
- return [self.pairs.get_json()]
-
-
-class FixedSizeListField(Field):
-
- def __init__(self, name, value_field, list_size, *, nullable=True,
- metadata=None):
- super().__init__(name, nullable=nullable,
- metadata=metadata)
- self.value_field = value_field
- self.list_size = list_size
-
- def _get_type(self):
- return OrderedDict([
- ('name', 'fixedsizelist'),
- ('listSize', self.list_size)
- ])
-
- def _get_children(self):
- return [self.value_field.get_json()]
-
- def generate_column(self, size, name=None):
- is_valid = self._make_is_valid(size)
- values = self.value_field.generate_column(size * self.list_size)
-
- if name is None:
- name = self.name
- return FixedSizeListColumn(name, size, is_valid, values)
-
-
-class FixedSizeListColumn(Column):
-
- def __init__(self, name, count, is_valid, values):
- super().__init__(name, count)
- self.is_valid = is_valid
- self.values = values
-
- def _get_buffers(self):
- return [
- ('VALIDITY', [int(v) for v in self.is_valid])
- ]
-
- def _get_children(self):
- return [self.values.get_json()]
-
-
-class StructField(Field):
-
- def __init__(self, name, fields, *, nullable=True,
- metadata=None):
- super().__init__(name, nullable=nullable,
- metadata=metadata)
- self.fields = fields
-
- def _get_type(self):
- return OrderedDict([
- ('name', 'struct')
- ])
-
- def _get_children(self):
- return [field.get_json() for field in self.fields]
-
- def generate_column(self, size, name=None):
- is_valid = self._make_is_valid(size)
-
- field_values = [field.generate_column(size) for field in self.fields]
- if name is None:
- name = self.name
- return StructColumn(name, size, is_valid, field_values)
-
-
-class _BaseUnionField(Field):
-
- def __init__(self, name, fields, type_ids=None, *, nullable=True,
- metadata=None):
- super().__init__(name, nullable=nullable, metadata=metadata)
- if type_ids is None:
- type_ids = list(range(fields))
- else:
- assert len(fields) == len(type_ids)
- self.fields = fields
- self.type_ids = type_ids
- assert all(x >= 0 for x in self.type_ids)
-
- def _get_type(self):
- return OrderedDict([
- ('name', 'union'),
- ('mode', self.mode),
- ('typeIds', self.type_ids),
- ])
-
- def _get_children(self):
- return [field.get_json() for field in self.fields]
-
- def _make_type_ids(self, size):
- return np.random.choice(self.type_ids, size)
-
-
-class SparseUnionField(_BaseUnionField):
- mode = 'SPARSE'
-
- def generate_column(self, size, name=None):
- array_type_ids = self._make_type_ids(size)
- field_values = [field.generate_column(size) for field in self.fields]
-
- if name is None:
- name = self.name
- return SparseUnionColumn(name, size, array_type_ids, field_values)
-
-
-class DenseUnionField(_BaseUnionField):
- mode = 'DENSE'
-
- def generate_column(self, size, name=None):
- # Reverse mapping {logical type id => physical child id}
- child_ids = [None] * (max(self.type_ids) + 1)
- for i, type_id in enumerate(self.type_ids):
- child_ids[type_id] = i
-
- array_type_ids = self._make_type_ids(size)
- offsets = []
- child_sizes = [0] * len(self.fields)
-
- for i in range(size):
- child_id = child_ids[array_type_ids[i]]
- offset = child_sizes[child_id]
- offsets.append(offset)
- child_sizes[child_id] = offset + 1
-
- field_values = [
- field.generate_column(child_size)
- for field, child_size in zip(self.fields, child_sizes)]
-
- if name is None:
- name = self.name
- return DenseUnionColumn(name, size, array_type_ids, offsets,
- field_values)
-
-
-class Dictionary(object):
-
- def __init__(self, id_, field, size, name=None, ordered=False):
- self.id_ = id_
- self.field = field
- self.values = field.generate_column(size=size, name=name)
- self.ordered = ordered
-
- def __len__(self):
- return len(self.values)
-
- def get_json(self):
- dummy_batch = RecordBatch(len(self.values), [self.values])
- return OrderedDict([
- ('id', self.id_),
- ('data', dummy_batch.get_json())
- ])
-
-
-class DictionaryField(Field):
-
- def __init__(self, name, index_field, dictionary, *, nullable=True,
- metadata=None):
- super().__init__(name, nullable=nullable,
- metadata=metadata)
- assert index_field.name == ''
- assert isinstance(index_field, IntegerField)
- assert isinstance(dictionary, Dictionary)
-
- self.index_field = index_field
- self.dictionary = dictionary
-
- def _get_type(self):
- return self.dictionary.field._get_type()
-
- def _get_children(self):
- return self.dictionary.field._get_children()
-
- def _get_dictionary(self):
- return OrderedDict([
- ('id', self.dictionary.id_),
- ('indexType', self.index_field._get_type()),
- ('isOrdered', self.dictionary.ordered)
- ])
-
- def generate_column(self, size, name=None):
- if name is None:
- name = self.name
- return self.index_field.generate_range(size, 0, len(self.dictionary),
- name=name)
-
-
-ExtensionType = namedtuple(
- 'ExtensionType', ['extension_name', 'serialized', 'storage_field'])
-
-
-class ExtensionField(Field):
-
- def __init__(self, name, extension_type, *, nullable=True, metadata=None):
- metadata = (metadata or []) + [
- ('ARROW:extension:name', extension_type.extension_name),
- ('ARROW:extension:metadata', extension_type.serialized),
- ]
- super().__init__(name, nullable=nullable, metadata=metadata)
- self.extension_type = extension_type
-
- def _get_type(self):
- return self.extension_type.storage_field._get_type()
-
- def _get_children(self):
- return self.extension_type.storage_field._get_children()
-
- def _get_dictionary(self):
- return self.extension_type.storage_field._get_dictionary()
-
- def generate_column(self, size, name=None):
- if name is None:
- name = self.name
- return self.extension_type.storage_field.generate_column(size, name)
-
-
-class StructColumn(Column):
-
- def __init__(self, name, count, is_valid, field_values):
- super().__init__(name, count)
- self.is_valid = is_valid
- self.field_values = field_values
-
- def _get_buffers(self):
- return [
- ('VALIDITY', [int(v) for v in self.is_valid])
- ]
-
- def _get_children(self):
- return [field.get_json() for field in self.field_values]
-
-
-class SparseUnionColumn(Column):
-
- def __init__(self, name, count, type_ids, field_values):
- super().__init__(name, count)
- self.type_ids = type_ids
- self.field_values = field_values
-
- def _get_buffers(self):
- return [
- ('TYPE_ID', [int(v) for v in self.type_ids])
- ]
-
- def _get_children(self):
- return [field.get_json() for field in self.field_values]
-
-
-class DenseUnionColumn(Column):
-
- def __init__(self, name, count, type_ids, offsets, field_values):
- super().__init__(name, count)
- self.type_ids = type_ids
- self.offsets = offsets
- self.field_values = field_values
-
- def _get_buffers(self):
- return [
- ('TYPE_ID', [int(v) for v in self.type_ids]),
- ('OFFSET', [int(v) for v in self.offsets]),
- ]
-
- def _get_children(self):
- return [field.get_json() for field in self.field_values]
-
-
-class RecordBatch(object):
-
- def __init__(self, count, columns):
- self.count = count
- self.columns = columns
-
- def get_json(self):
- return OrderedDict([
- ('count', self.count),
- ('columns', [col.get_json() for col in self.columns])
- ])
-
-
-class File(object):
-
- def __init__(self, name, schema, batches, dictionaries=None,
- skip=None, path=None):
- self.name = name
- self.schema = schema
- self.dictionaries = dictionaries or []
- self.batches = batches
- self.skip = set()
- self.path = path
- if skip:
- self.skip.update(skip)
-
- def get_json(self):
- entries = [
- ('schema', self.schema.get_json())
- ]
-
- if len(self.dictionaries) > 0:
- entries.append(('dictionaries',
- [dictionary.get_json()
- for dictionary in self.dictionaries]))
-
- entries.append(('batches', [batch.get_json()
- for batch in self.batches]))
- return OrderedDict(entries)
-
- def write(self, path):
- with open(path, 'wb') as f:
- f.write(json.dumps(self.get_json(), indent=2).encode('utf-8'))
- self.path = path
-
- def skip_category(self, category):
- """Skip this test for the given category.
-
- Category should be SKIP_ARROW or SKIP_FLIGHT.
- """
- self.skip.add(category)
- return self
-
-
-def get_field(name, type_, **kwargs):
- if type_ == 'binary':
- return BinaryField(name, **kwargs)
- elif type_ == 'utf8':
- return StringField(name, **kwargs)
- elif type_ == 'largebinary':
- return LargeBinaryField(name, **kwargs)
- elif type_ == 'largeutf8':
- return LargeStringField(name, **kwargs)
- elif type_.startswith('fixedsizebinary_'):
- byte_width = int(type_.split('_')[1])
- return FixedSizeBinaryField(name, byte_width=byte_width, **kwargs)
-
- dtype = np.dtype(type_)
-
- if dtype.kind in ('i', 'u'):
- signed = dtype.kind == 'i'
- bit_width = dtype.itemsize * 8
- return IntegerField(name, signed, bit_width, **kwargs)
- elif dtype.kind == 'f':
- bit_width = dtype.itemsize * 8
- return FloatingPointField(name, bit_width, **kwargs)
- elif dtype.kind == 'b':
- return BooleanField(name, **kwargs)
- else:
- raise TypeError(dtype)
-
-
-def _generate_file(name, fields, batch_sizes, dictionaries=None, skip=None,
- metadata=None):
- schema = Schema(fields, metadata=metadata)
- batches = []
- for size in batch_sizes:
- columns = []
- for field in fields:
- col = field.generate_column(size)
- columns.append(col)
-
- batches.append(RecordBatch(size, columns))
-
- return File(name, schema, batches, dictionaries, skip=skip)
-
-
-def generate_custom_metadata_case():
- def meta(items):
- # Generate a simple block of metadata where each value is '{}'.
- # Keys are delimited by whitespace in `items`.
- return [(k, '{}') for k in items.split()]
-
- fields = [
- get_field('sort_of_pandas', 'int8', metadata=meta('pandas')),
-
- get_field('lots_of_meta', 'int8', metadata=meta('a b c d .. w x y z')),
-
- get_field(
- 'unregistered_extension', 'int8',
- metadata=[
- ('ARROW:extension:name', '!nonexistent'),
- ('ARROW:extension:metadata', ''),
- ('ARROW:integration:allow_unregistered_extension', 'true'),
- ]),
-
- ListField('list_with_odd_values',
- get_field('item', 'int32', metadata=meta('odd_values'))),
- ]
-
- batch_sizes = [1]
- return _generate_file('custom_metadata', fields, batch_sizes,
- metadata=meta('schema_custom_0 schema_custom_1'))
-
-
-def generate_duplicate_fieldnames_case():
- fields = [
- get_field('ints', 'int8'),
- get_field('ints', 'int32'),
-
- StructField('struct', [get_field('', 'int32'), get_field('', 'utf8')]),
- ]
-
- batch_sizes = [1]
- return _generate_file('duplicate_fieldnames', fields, batch_sizes)
-
-
-def generate_primitive_case(batch_sizes, name='primitive'):
- types = ['bool', 'int8', 'int16', 'int32', 'int64',
- 'uint8', 'uint16', 'uint32', 'uint64',
- 'float32', 'float64', 'binary', 'utf8',
- 'fixedsizebinary_19', 'fixedsizebinary_120']
-
- fields = []
-
- for type_ in types:
- fields.append(get_field(type_ + "_nullable", type_, nullable=True))
- fields.append(get_field(type_ + "_nonnullable", type_, nullable=False))
-
- return _generate_file(name, fields, batch_sizes)
-
-
-def generate_primitive_large_offsets_case(batch_sizes):
- types = ['largebinary', 'largeutf8']
-
- fields = []
-
- for type_ in types:
- fields.append(get_field(type_ + "_nullable", type_, nullable=True))
- fields.append(get_field(type_ + "_nonnullable", type_, nullable=False))
-
- return _generate_file('primitive_large_offsets', fields, batch_sizes)
-
-
-def generate_null_case(batch_sizes):
- # Interleave null with non-null types to ensure the appropriate number of
- # buffers (0) is read and written
- fields = [
- NullField(name='f0'),
- get_field('f1', 'int32'),
- NullField(name='f2'),
- get_field('f3', 'float64'),
- NullField(name='f4')
- ]
- return _generate_file('null', fields, batch_sizes)
-
-
-def generate_null_trivial_case(batch_sizes):
- # Generate a case with no buffers
- fields = [
- NullField(name='f0'),
- ]
- return _generate_file('null_trivial', fields, batch_sizes)
-
-
-def generate_decimal128_case():
- fields = [
- DecimalField(name='f{}'.format(i), precision=precision, scale=2,
- bit_width=128)
- for i, precision in enumerate(range(3, 39))
- ]
-
- possible_batch_sizes = 7, 10
- batch_sizes = [possible_batch_sizes[i % 2] for i in range(len(fields))]
- # 'decimal' is the original name for the test, and it must match
- # provide "gold" files that test backwards compatibility, so they
- # can be appropriately skipped.
- return _generate_file('decimal', fields, batch_sizes)
-
-
-def generate_decimal256_case():
- fields = [
- DecimalField(name='f{}'.format(i), precision=precision, scale=5,
- bit_width=256)
- for i, precision in enumerate(range(37, 70))
- ]
-
- possible_batch_sizes = 7, 10
- batch_sizes = [possible_batch_sizes[i % 2] for i in range(len(fields))]
- return _generate_file('decimal256', fields, batch_sizes)
-
-
-def generate_datetime_case():
- fields = [
- DateField('f0', DateField.DAY),
- DateField('f1', DateField.MILLISECOND),
- TimeField('f2', 's'),
- TimeField('f3', 'ms'),
- TimeField('f4', 'us'),
- TimeField('f5', 'ns'),
- TimestampField('f6', 's'),
- TimestampField('f7', 'ms'),
- TimestampField('f8', 'us'),
- TimestampField('f9', 'ns'),
- TimestampField('f10', 'ms', tz=None),
- TimestampField('f11', 's', tz='UTC'),
- TimestampField('f12', 'ms', tz='US/Eastern'),
- TimestampField('f13', 'us', tz='Europe/Paris'),
- TimestampField('f14', 'ns', tz='US/Pacific'),
- ]
-
- batch_sizes = [7, 10]
- return _generate_file("datetime", fields, batch_sizes)
-
-
-def generate_interval_case():
- fields = [
- DurationIntervalField('f1', 's'),
- DurationIntervalField('f2', 'ms'),
- DurationIntervalField('f3', 'us'),
- DurationIntervalField('f4', 'ns'),
- YearMonthIntervalField('f5'),
- DayTimeIntervalField('f6'),
- ]
-
- batch_sizes = [7, 10]
- return _generate_file("interval", fields, batch_sizes)
-
-
-def generate_map_case():
- fields = [
- MapField('map_nullable', get_field('key', 'utf8', nullable=False),
- get_field('value', 'int32')),
- ]
-
- batch_sizes = [7, 10]
- return _generate_file("map", fields, batch_sizes)
-
-
-def generate_non_canonical_map_case():
- fields = [
- MapField('map_other_names',
- get_field('some_key', 'utf8', nullable=False),
- get_field('some_value', 'int32'),
- entries_name='some_entries'),
- ]
-
- batch_sizes = [7]
- return _generate_file("map_non_canonical", fields, batch_sizes)
-
-
-def generate_nested_case():
- fields = [
- ListField('list_nullable', get_field('item', 'int32')),
- FixedSizeListField('fixedsizelist_nullable',
- get_field('item', 'int32'), 4),
- StructField('struct_nullable', [get_field('f1', 'int32'),
- get_field('f2', 'utf8')]),
- # Fails on Go (ARROW-8452)
- # ListField('list_nonnullable', get_field('item', 'int32'),
- # nullable=False),
- ]
-
- batch_sizes = [7, 10]
- return _generate_file("nested", fields, batch_sizes)
-
-
-def generate_recursive_nested_case():
- fields = [
- ListField('lists_list',
- ListField('inner_list', get_field('item', 'int16'))),
- ListField('structs_list',
- StructField('inner_struct',
- [get_field('f1', 'int32'),
- get_field('f2', 'utf8')])),
- ]
-
- batch_sizes = [7, 10]
- return _generate_file("recursive_nested", fields, batch_sizes)
-
-
-def generate_nested_large_offsets_case():
- fields = [
- LargeListField('large_list_nullable', get_field('item', 'int32')),
- LargeListField('large_list_nonnullable',
- get_field('item', 'int32'), nullable=False),
- LargeListField('large_list_nested',
- ListField('inner_list', get_field('item', 'int16'))),
- ]
-
- batch_sizes = [0, 13]
- return _generate_file("nested_large_offsets", fields, batch_sizes)
-
-
-def generate_unions_case():
- fields = [
- SparseUnionField('sparse', [get_field('f1', 'int32'),
- get_field('f2', 'utf8')],
- type_ids=[5, 7]),
- DenseUnionField('dense', [get_field('f1', 'int16'),
- get_field('f2', 'binary')],
- type_ids=[10, 20]),
- SparseUnionField('sparse', [get_field('f1', 'float32', nullable=False),
- get_field('f2', 'bool')],
- type_ids=[5, 7], nullable=False),
- DenseUnionField('dense', [get_field('f1', 'uint8', nullable=False),
- get_field('f2', 'uint16'),
- NullField('f3')],
- type_ids=[42, 43, 44], nullable=False),
- ]
-
- batch_sizes = [0, 11]
- return _generate_file("union", fields, batch_sizes)
-
-
-def generate_dictionary_case():
- dict0 = Dictionary(0, StringField('dictionary1'), size=10, name='DICT0')
- dict1 = Dictionary(1, StringField('dictionary1'), size=5, name='DICT1')
- dict2 = Dictionary(2, get_field('dictionary2', 'int64'),
- size=50, name='DICT2')
-
- fields = [
- DictionaryField('dict0', get_field('', 'int8'), dict0),
- DictionaryField('dict1', get_field('', 'int32'), dict1),
- DictionaryField('dict2', get_field('', 'int16'), dict2)
- ]
- batch_sizes = [7, 10]
- return _generate_file("dictionary", fields, batch_sizes,
- dictionaries=[dict0, dict1, dict2])
-
-
-def generate_dictionary_unsigned_case():
- dict0 = Dictionary(0, StringField('dictionary0'), size=5, name='DICT0')
- dict1 = Dictionary(1, StringField('dictionary1'), size=5, name='DICT1')
- dict2 = Dictionary(2, StringField('dictionary2'), size=5, name='DICT2')
-
- # TODO: JavaScript does not support uint64 dictionary indices, so disabled
- # for now
-
- # dict3 = Dictionary(3, StringField('dictionary3'), size=5, name='DICT3')
- fields = [
- DictionaryField('f0', get_field('', 'uint8'), dict0),
- DictionaryField('f1', get_field('', 'uint16'), dict1),
- DictionaryField('f2', get_field('', 'uint32'), dict2),
- # DictionaryField('f3', get_field('', 'uint64'), dict3)
- ]
- batch_sizes = [7, 10]
- return _generate_file("dictionary_unsigned", fields, batch_sizes,
- dictionaries=[dict0, dict1, dict2])
-
-
-def generate_nested_dictionary_case():
- dict0 = Dictionary(0, StringField('str'), size=10, name='DICT0')
-
- list_of_dict = ListField(
- 'list',
- DictionaryField('str_dict', get_field('', 'int8'), dict0))
- dict1 = Dictionary(1, list_of_dict, size=30, name='DICT1')
-
- struct_of_dict = StructField('struct', [
- DictionaryField('str_dict_a', get_field('', 'int8'), dict0),
- DictionaryField('str_dict_b', get_field('', 'int8'), dict0)
- ])
- dict2 = Dictionary(2, struct_of_dict, size=30, name='DICT2')
-
- fields = [
- DictionaryField('list_dict', get_field('', 'int8'), dict1),
- DictionaryField('struct_dict', get_field('', 'int8'), dict2)
- ]
-
- batch_sizes = [10, 13]
- return _generate_file("nested_dictionary", fields, batch_sizes,
- dictionaries=[dict0, dict1, dict2])
-
-
-def generate_extension_case():
- dict0 = Dictionary(0, StringField('dictionary0'), size=5, name='DICT0')
-
- uuid_type = ExtensionType('uuid', 'uuid-serialized',
- FixedSizeBinaryField('', 16))
- dict_ext_type = ExtensionType(
- 'dict-extension', 'dict-extension-serialized',
- DictionaryField('str_dict', get_field('', 'int8'), dict0))
-
- fields = [
- ExtensionField('uuids', uuid_type),
- ExtensionField('dict_exts', dict_ext_type),
- ]
-
- batch_sizes = [0, 13]
- return _generate_file("extension", fields, batch_sizes,
- dictionaries=[dict0])
-
-
-def get_generated_json_files(tempdir=None):
- tempdir = tempdir or tempfile.mkdtemp(prefix='arrow-integration-')
-
- def _temp_path():
- return
-
- file_objs = [
- generate_primitive_case([], name='primitive_no_batches'),
- generate_primitive_case([17, 20], name='primitive'),
- generate_primitive_case([0, 0, 0], name='primitive_zerolength'),
-
- generate_primitive_large_offsets_case([17, 20])
- .skip_category('Go')
- .skip_category('JS'),
-
- generate_null_case([10, 0])
- .skip_category('JS') # TODO(ARROW-7900)
- .skip_category('Go'), # TODO(ARROW-7901)
-
- generate_null_trivial_case([0, 0])
- .skip_category('JS') # TODO(ARROW-7900)
- .skip_category('Go'), # TODO(ARROW-7901)
-
- generate_decimal128_case()
- .skip_category('Go') # TODO(ARROW-7948): Decimal + Go
- .skip_category('Rust'),
-
- generate_decimal256_case()
- .skip_category('Go') # TODO(ARROW-7948): Decimal + Go
- .skip_category('JS')
- .skip_category('Rust'),
-
- generate_datetime_case(),
-
- generate_interval_case()
- .skip_category('JS') # TODO(ARROW-5239): Intervals + JS
- .skip_category('Rust'),
-
- generate_map_case()
- .skip_category('Go') # TODO(ARROW-5620): Map + Go
- .skip_category('Rust'),
-
- generate_non_canonical_map_case()
- .skip_category('Go') # TODO(ARROW-5620)
- .skip_category('Java') # TODO(ARROW-8715)
- .skip_category('JS') # TODO(ARROW-8716)
- .skip_category('Rust'),
-
- generate_nested_case(),
-
- generate_recursive_nested_case()
- .skip_category('Go'), # TODO(ARROW-8453)
-
- generate_nested_large_offsets_case()
- .skip_category('Go')
- .skip_category('JS')
- .skip_category('Rust'),
-
- generate_unions_case()
- .skip_category('Go')
- .skip_category('JS')
- .skip_category('Rust'),
-
- generate_custom_metadata_case()
- .skip_category('Go')
- .skip_category('JS'),
-
- generate_duplicate_fieldnames_case()
- .skip_category('Go')
- .skip_category('JS'),
-
- # TODO(ARROW-3039, ARROW-5267): Dictionaries in GO
- generate_dictionary_case()
- .skip_category('Go'),
-
- generate_dictionary_unsigned_case()
- .skip_category('Go') # TODO(ARROW-9378)
- .skip_category('Java'), # TODO(ARROW-9377)
-
- generate_nested_dictionary_case()
- .skip_category('Go')
- .skip_category('Java') # TODO(ARROW-7779)
- .skip_category('JS')
- .skip_category('Rust'),
-
- generate_extension_case()
- .skip_category('Go')
- .skip_category('JS')
- .skip_category('Rust'),
- ]
-
- generated_paths = []
- for file_obj in file_objs:
- out_path = os.path.join(tempdir, 'generated_' +
- file_obj.name + '.json')
- file_obj.write(out_path)
- generated_paths.append(file_obj)
-
- return generated_paths
diff --git a/dev/archery/archery/integration/runner.py b/dev/archery/archery/integration/runner.py
deleted file mode 100644
index 8aef163..0000000
--- a/dev/archery/archery/integration/runner.py
+++ /dev/null
@@ -1,419 +0,0 @@
-# licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-from collections import namedtuple
-from concurrent.futures import ThreadPoolExecutor
-from functools import partial
-import glob
-import gzip
-import itertools
-import os
-import sys
-import tempfile
-import traceback
-
-from .scenario import Scenario
-from .tester_cpp import CPPTester
-from .tester_go import GoTester
-from .tester_rust import RustTester
-from .tester_java import JavaTester
-from .tester_js import JSTester
-from .util import (ARROW_ROOT_DEFAULT, guid, SKIP_ARROW, SKIP_FLIGHT,
- printer)
-from . import datagen
-
-
-Failure = namedtuple('Failure',
- ('test_case', 'producer', 'consumer', 'exc_info'))
-
-log = printer.print
-
-
-class Outcome:
- def __init__(self):
- self.failure = None
- self.skipped = False
-
-
-class IntegrationRunner(object):
-
- def __init__(self, json_files, flight_scenarios, testers, tempdir=None,
- debug=False, stop_on_error=True, gold_dirs=None,
- serial=False, match=None, **unused_kwargs):
- self.json_files = json_files
- self.flight_scenarios = flight_scenarios
- self.testers = testers
- self.temp_dir = tempdir or tempfile.mkdtemp()
- self.debug = debug
- self.stop_on_error = stop_on_error
- self.serial = serial
- self.gold_dirs = gold_dirs
- self.failures = []
- self.match = match
-
- if self.match is not None:
- print("-- Only running tests with {} in their name"
- .format(self.match))
- self.json_files = [json_file for json_file in self.json_files
- if self.match in json_file.name]
-
- def run(self):
- """
- Run Arrow IPC integration tests for the matrix of enabled
- implementations.
- """
- for producer, consumer in itertools.product(
- filter(lambda t: t.PRODUCER, self.testers),
- filter(lambda t: t.CONSUMER, self.testers)):
- self._compare_implementations(
- producer, consumer, self._produce_consume,
- self.json_files)
- if self.gold_dirs:
- for gold_dir, consumer in itertools.product(
- self.gold_dirs,
- filter(lambda t: t.CONSUMER, self.testers)):
- log('\n\n\n\n')
- log('******************************************************')
- log('Tests against golden files in {}'.format(gold_dir))
- log('******************************************************')
-
- def run_gold(producer, consumer, outcome, test_case):
- self._run_gold(gold_dir, producer, consumer, outcome,
- test_case)
- self._compare_implementations(
- consumer, consumer, run_gold,
- self._gold_tests(gold_dir))
-
- def run_flight(self):
- """
- Run Arrow Flight integration tests for the matrix of enabled
- implementations.
- """
- servers = filter(lambda t: t.FLIGHT_SERVER, self.testers)
- clients = filter(lambda t: (t.FLIGHT_CLIENT and t.CONSUMER),
- self.testers)
- for server, client in itertools.product(servers, clients):
- self._compare_flight_implementations(server, client)
-
- def _gold_tests(self, gold_dir):
- prefix = os.path.basename(os.path.normpath(gold_dir))
- SUFFIX = ".json.gz"
- golds = [jf for jf in os.listdir(gold_dir) if jf.endswith(SUFFIX)]
- for json_path in golds:
- name = json_path[json_path.index('_')+1: -len(SUFFIX)]
- base_name = prefix + "_" + name + ".gold.json"
- out_path = os.path.join(self.temp_dir, base_name)
- with gzip.open(os.path.join(gold_dir, json_path)) as i:
- with open(out_path, "wb") as out:
- out.write(i.read())
-
- try:
- skip = next(f for f in self.json_files
- if f.name == name).skip
- except StopIteration:
- skip = set()
- if name == 'union' and prefix == '0.17.1':
- skip.add("Java")
- if prefix == '1.0.0-bigendian' or prefix == '1.0.0-littleendian':
- skip.add("Go")
- skip.add("Java")
- skip.add("JS")
- skip.add("Rust")
- if prefix == '2.0.0-compression':
- skip.add("JS")
- skip.add("Rust")
-
- # See https://github.com/apache/arrow/pull/9822 for how to
- # disable specific compression type tests.
-
- if prefix == '4.0.0-shareddict':
- skip.add("Go")
-
- yield datagen.File(name, None, None, skip=skip, path=out_path)
-
- def _run_test_cases(self, producer, consumer, case_runner,
- test_cases):
- def case_wrapper(test_case):
- with printer.cork():
- return case_runner(test_case)
-
- if self.failures and self.stop_on_error:
- return
-
- if self.serial:
- for outcome in map(case_wrapper, test_cases):
- if outcome.failure is not None:
- self.failures.append(outcome.failure)
- if self.stop_on_error:
- break
-
- else:
- with ThreadPoolExecutor() as executor:
- for outcome in executor.map(case_wrapper, test_cases):
- if outcome.failure is not None:
- self.failures.append(outcome.failure)
- if self.stop_on_error:
- break
-
- def _compare_implementations(
- self, producer, consumer, run_binaries, test_cases):
- """
- Compare Arrow IPC for two implementations (one producer, one consumer).
- """
- log('##########################################################')
- log('IPC: {0} producing, {1} consuming'
- .format(producer.name, consumer.name))
- log('##########################################################')
-
- case_runner = partial(self._run_ipc_test_case,
- producer, consumer, run_binaries)
- self._run_test_cases(producer, consumer, case_runner, test_cases)
-
- def _run_ipc_test_case(self, producer, consumer, run_binaries, test_case):
- """
- Run one IPC test case.
- """
- outcome = Outcome()
-
- json_path = test_case.path
- log('==========================================================')
- log('Testing file {0}'.format(json_path))
- log('==========================================================')
-
- if producer.name in test_case.skip:
- log('-- Skipping test because producer {0} does '
- 'not support'.format(producer.name))
- outcome.skipped = True
-
- elif consumer.name in test_case.skip:
- log('-- Skipping test because consumer {0} does '
- 'not support'.format(consumer.name))
- outcome.skipped = True
-
- elif SKIP_ARROW in test_case.skip:
- log('-- Skipping test')
- outcome.skipped = True
-
- else:
- try:
- run_binaries(producer, consumer, outcome, test_case)
- except Exception:
- traceback.print_exc(file=printer.stdout)
- outcome.failure = Failure(test_case, producer, consumer,
- sys.exc_info())
-
- return outcome
-
- def _produce_consume(self, producer, consumer, outcome, test_case):
- # Make the random access file
- json_path = test_case.path
- file_id = guid()[:8]
- name = os.path.splitext(os.path.basename(json_path))[0]
-
- producer_file_path = os.path.join(self.temp_dir, file_id + '_' +
- name + '.json_as_file')
- producer_stream_path = os.path.join(self.temp_dir, file_id + '_' +
- name + '.producer_file_as_stream')
- consumer_file_path = os.path.join(self.temp_dir, file_id + '_' +
- name + '.consumer_stream_as_file')
-
- log('-- Creating binary inputs')
- producer.json_to_file(json_path, producer_file_path)
-
- # Validate the file
- log('-- Validating file')
- consumer.validate(json_path, producer_file_path)
-
- log('-- Validating stream')
- producer.file_to_stream(producer_file_path, producer_stream_path)
- consumer.stream_to_file(producer_stream_path, consumer_file_path)
- consumer.validate(json_path, consumer_file_path)
-
- def _run_gold(self, gold_dir, producer, consumer, outcome, test_case):
- json_path = test_case.path
-
- # Validate the file
- log('-- Validating file')
- producer_file_path = os.path.join(
- gold_dir, "generated_" + test_case.name + ".arrow_file")
- consumer.validate(json_path, producer_file_path)
-
- log('-- Validating stream')
- consumer_stream_path = os.path.join(
- gold_dir, "generated_" + test_case.name + ".stream")
- file_id = guid()[:8]
- name = os.path.splitext(os.path.basename(json_path))[0]
-
- consumer_file_path = os.path.join(self.temp_dir, file_id + '_' +
- name + '.consumer_stream_as_file')
-
- consumer.stream_to_file(consumer_stream_path, consumer_file_path)
- consumer.validate(json_path, consumer_file_path)
-
- def _compare_flight_implementations(self, producer, consumer):
- log('##########################################################')
- log('Flight: {0} serving, {1} requesting'
- .format(producer.name, consumer.name))
- log('##########################################################')
-
- case_runner = partial(self._run_flight_test_case, producer, consumer)
- self._run_test_cases(producer, consumer, case_runner,
- self.json_files + self.flight_scenarios)
-
- def _run_flight_test_case(self, producer, consumer, test_case):
- """
- Run one Flight test case.
- """
- outcome = Outcome()
-
- log('=' * 58)
- log('Testing file {0}'.format(test_case.name))
- log('=' * 58)
-
- if producer.name in test_case.skip:
- log('-- Skipping test because producer {0} does '
- 'not support'.format(producer.name))
- outcome.skipped = True
-
- elif consumer.name in test_case.skip:
- log('-- Skipping test because consumer {0} does '
- 'not support'.format(consumer.name))
- outcome.skipped = True
-
- elif SKIP_FLIGHT in test_case.skip:
- log('-- Skipping test')
- outcome.skipped = True
-
- else:
- try:
- if isinstance(test_case, Scenario):
- server = producer.flight_server(test_case.name)
- client_args = {'scenario_name': test_case.name}
- else:
- server = producer.flight_server()
- client_args = {'json_path': test_case.path}
-
- with server as port:
- # Have the client upload the file, then download and
- # compare
- consumer.flight_request(port, **client_args)
- except Exception:
- traceback.print_exc(file=printer.stdout)
- outcome.failure = Failure(test_case, producer, consumer,
- sys.exc_info())
-
- return outcome
-
-
-def get_static_json_files():
- glob_pattern = os.path.join(ARROW_ROOT_DEFAULT,
- 'integration', 'data', '*.json')
- return [
- datagen.File(name=os.path.basename(p), path=p, skip=set(),
- schema=None, batches=None)
- for p in glob.glob(glob_pattern)
- ]
-
-
-def run_all_tests(with_cpp=True, with_java=True, with_js=True,
- with_go=True, with_rust=False, run_flight=False,
- tempdir=None, **kwargs):
- tempdir = tempdir or tempfile.mkdtemp(prefix='arrow-integration-')
-
- testers = []
-
- if with_cpp:
- testers.append(CPPTester(**kwargs))
-
- if with_java:
- testers.append(JavaTester(**kwargs))
-
- if with_js:
- testers.append(JSTester(**kwargs))
-
- if with_go:
- testers.append(GoTester(**kwargs))
-
- if with_rust:
- testers.append(RustTester(**kwargs))
-
- static_json_files = get_static_json_files()
- generated_json_files = datagen.get_generated_json_files(tempdir=tempdir)
- json_files = static_json_files + generated_json_files
-
- # Additional integration test cases for Arrow Flight.
- flight_scenarios = [
- Scenario(
- "auth:basic_proto",
- description="Authenticate using the BasicAuth protobuf."),
- Scenario(
- "middleware",
- description="Ensure headers are propagated via middleware.",
- skip={"Rust"} # TODO(ARROW-10961): tonic upgrade needed
- ),
- ]
-
- runner = IntegrationRunner(json_files, flight_scenarios, testers, **kwargs)
- runner.run()
- if run_flight:
- runner.run_flight()
-
- fail_count = 0
- if runner.failures:
- log("################# FAILURES #################")
- for test_case, producer, consumer, exc_info in runner.failures:
- fail_count += 1
- log("FAILED TEST:", end=" ")
- log(test_case.name, producer.name, "producing, ",
- consumer.name, "consuming")
- if exc_info:
- traceback.print_exception(*exc_info)
- log()
-
- log(fail_count, "failures")
- if fail_count > 0:
- sys.exit(1)
-
-
-def write_js_test_json(directory):
- datagen.generate_map_case().write(
- os.path.join(directory, 'map.json')
- )
- datagen.generate_nested_case().write(
- os.path.join(directory, 'nested.json')
- )
- datagen.generate_decimal_case().write(
- os.path.join(directory, 'decimal.json')
- )
- datagen.generate_datetime_case().write(
- os.path.join(directory, 'datetime.json')
- )
- datagen.generate_dictionary_case().write(
- os.path.join(directory, 'dictionary.json')
- )
- datagen.generate_dictionary_unsigned_case().write(
- os.path.join(directory, 'dictionary_unsigned.json')
- )
- datagen.generate_primitive_case([]).write(
- os.path.join(directory, 'primitive_no_batches.json')
- )
- datagen.generate_primitive_case([7, 10]).write(
- os.path.join(directory, 'primitive.json')
- )
- datagen.generate_primitive_case([0, 0, 0]).write(
- os.path.join(directory, 'primitive-empty.json')
- )
diff --git a/dev/archery/archery/integration/scenario.py b/dev/archery/archery/integration/scenario.py
deleted file mode 100644
index 1fcbca6..0000000
--- a/dev/archery/archery/integration/scenario.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-
-class Scenario:
- """
- An integration test scenario for Arrow Flight.
-
- Does not correspond to a particular IPC JSON file.
- """
-
- def __init__(self, name, description, skip=None):
- self.name = name
- self.description = description
- self.skip = skip or set()
diff --git a/dev/archery/archery/integration/tester.py b/dev/archery/archery/integration/tester.py
deleted file mode 100644
index 122e4f2..0000000
--- a/dev/archery/archery/integration/tester.py
+++ /dev/null
@@ -1,62 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-# Base class for language-specific integration test harnesses
-
-import subprocess
-
-from .util import log
-
-
-class Tester(object):
- PRODUCER = False
- CONSUMER = False
- FLIGHT_SERVER = False
- FLIGHT_CLIENT = False
-
- def __init__(self, debug=False, **args):
- self.args = args
- self.debug = debug
-
- def run_shell_command(self, cmd):
- cmd = ' '.join(cmd)
- if self.debug:
- log(cmd)
- subprocess.check_call(cmd, shell=True)
-
- def json_to_file(self, json_path, arrow_path):
- raise NotImplementedError
-
- def stream_to_file(self, stream_path, file_path):
- raise NotImplementedError
-
- def file_to_stream(self, file_path, stream_path):
- raise NotImplementedError
-
- def validate(self, json_path, arrow_path):
- raise NotImplementedError
-
- def flight_server(self, scenario_name=None):
- """Start the Flight server on a free port.
-
- This should be a context manager that returns the port as the
- managed object, and cleans up the server on exit.
- """
- raise NotImplementedError
-
- def flight_request(self, port, json_path=None, scenario_name=None):
- raise NotImplementedError
diff --git a/dev/archery/archery/integration/tester_cpp.py b/dev/archery/archery/integration/tester_cpp.py
deleted file mode 100644
index d35c955..0000000
--- a/dev/archery/archery/integration/tester_cpp.py
+++ /dev/null
@@ -1,116 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-import contextlib
-import os
-import subprocess
-
-from .tester import Tester
-from .util import run_cmd, ARROW_ROOT_DEFAULT, log
-
-
-class CPPTester(Tester):
- PRODUCER = True
- CONSUMER = True
- FLIGHT_SERVER = True
- FLIGHT_CLIENT = True
-
- EXE_PATH = os.environ.get(
- 'ARROW_CPP_EXE_PATH',
- os.path.join(ARROW_ROOT_DEFAULT, 'cpp/build/debug'))
-
- CPP_INTEGRATION_EXE = os.path.join(EXE_PATH, 'arrow-json-integration-test')
- STREAM_TO_FILE = os.path.join(EXE_PATH, 'arrow-stream-to-file')
- FILE_TO_STREAM = os.path.join(EXE_PATH, 'arrow-file-to-stream')
-
- FLIGHT_SERVER_CMD = [
- os.path.join(EXE_PATH, 'flight-test-integration-server')]
- FLIGHT_CLIENT_CMD = [
- os.path.join(EXE_PATH, 'flight-test-integration-client'),
- "-host", "localhost"]
-
- name = 'C++'
-
- def _run(self, arrow_path=None, json_path=None, command='VALIDATE'):
- cmd = [self.CPP_INTEGRATION_EXE, '--integration']
-
- if arrow_path is not None:
- cmd.append('--arrow=' + arrow_path)
-
- if json_path is not None:
- cmd.append('--json=' + json_path)
-
- cmd.append('--mode=' + command)
-
- if self.debug:
- log(' '.join(cmd))
-
- run_cmd(cmd)
-
- def validate(self, json_path, arrow_path):
- return self._run(arrow_path, json_path, 'VALIDATE')
-
- def json_to_file(self, json_path, arrow_path):
- return self._run(arrow_path, json_path, 'JSON_TO_ARROW')
-
- def stream_to_file(self, stream_path, file_path):
- cmd = [self.STREAM_TO_FILE, '<', stream_path, '>', file_path]
- self.run_shell_command(cmd)
-
- def file_to_stream(self, file_path, stream_path):
- cmd = [self.FILE_TO_STREAM, file_path, '>', stream_path]
- self.run_shell_command(cmd)
-
- @contextlib.contextmanager
- def flight_server(self, scenario_name=None):
- cmd = self.FLIGHT_SERVER_CMD + ['-port=0']
- if scenario_name:
- cmd = cmd + ["-scenario", scenario_name]
- if self.debug:
- log(' '.join(cmd))
- server = subprocess.Popen(cmd,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
- try:
- output = server.stdout.readline().decode()
- if not output.startswith("Server listening on localhost:"):
- server.kill()
- out, err = server.communicate()
- raise RuntimeError(
- "Flight-C++ server did not start properly, "
- "stdout:\n{}\n\nstderr:\n{}\n"
- .format(output + out.decode(), err.decode()))
- port = int(output.split(":")[1])
- yield port
- finally:
- server.kill()
- server.wait(5)
-
- def flight_request(self, port, json_path=None, scenario_name=None):
- cmd = self.FLIGHT_CLIENT_CMD + [
- '-port=' + str(port),
- ]
- if json_path:
- cmd.extend(('-path', json_path))
- elif scenario_name:
- cmd.extend(('-scenario', scenario_name))
- else:
- raise TypeError("Must provide one of json_path or scenario_name")
-
- if self.debug:
- log(' '.join(cmd))
- run_cmd(cmd)
diff --git a/dev/archery/archery/integration/tester_go.py b/dev/archery/archery/integration/tester_go.py
deleted file mode 100644
index ea799c5..0000000
--- a/dev/archery/archery/integration/tester_go.py
+++ /dev/null
@@ -1,67 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-import os
-
-from .tester import Tester
-from .util import run_cmd, log
-
-
-class GoTester(Tester):
- PRODUCER = True
- CONSUMER = True
-
- # FIXME(sbinet): revisit for Go modules
- HOME = os.getenv('HOME', '~')
- GOPATH = os.getenv('GOPATH', os.path.join(HOME, 'go'))
- GOBIN = os.environ.get('GOBIN', os.path.join(GOPATH, 'bin'))
-
- GO_INTEGRATION_EXE = os.path.join(GOBIN, 'arrow-json-integration-test')
- STREAM_TO_FILE = os.path.join(GOBIN, 'arrow-stream-to-file')
- FILE_TO_STREAM = os.path.join(GOBIN, 'arrow-file-to-stream')
-
- name = 'Go'
-
- def _run(self, arrow_path=None, json_path=None, command='VALIDATE'):
- cmd = [self.GO_INTEGRATION_EXE]
-
- if arrow_path is not None:
- cmd.extend(['-arrow', arrow_path])
-
- if json_path is not None:
- cmd.extend(['-json', json_path])
-
- cmd.extend(['-mode', command])
-
- if self.debug:
- log(' '.join(cmd))
-
- run_cmd(cmd)
-
- def validate(self, json_path, arrow_path):
- return self._run(arrow_path, json_path, 'VALIDATE')
-
- def json_to_file(self, json_path, arrow_path):
- return self._run(arrow_path, json_path, 'JSON_TO_ARROW')
-
- def stream_to_file(self, stream_path, file_path):
- cmd = [self.STREAM_TO_FILE, '<', stream_path, '>', file_path]
- self.run_shell_command(cmd)
-
- def file_to_stream(self, file_path, stream_path):
- cmd = [self.FILE_TO_STREAM, file_path, '>', stream_path]
- self.run_shell_command(cmd)
diff --git a/dev/archery/archery/integration/tester_java.py b/dev/archery/archery/integration/tester_java.py
deleted file mode 100644
index f283f6c..0000000
--- a/dev/archery/archery/integration/tester_java.py
+++ /dev/null
@@ -1,140 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-import contextlib
-import os
-import subprocess
-
-from .tester import Tester
-from .util import run_cmd, ARROW_ROOT_DEFAULT, log
-
-
-def load_version_from_pom():
- import xml.etree.ElementTree as ET
- tree = ET.parse(os.path.join(ARROW_ROOT_DEFAULT, 'java', 'pom.xml'))
- tag_pattern = '{http://maven.apache.org/POM/4.0.0}version'
- version_tag = list(tree.getroot().findall(tag_pattern))[0]
- return version_tag.text
-
-
-class JavaTester(Tester):
- PRODUCER = True
- CONSUMER = True
- FLIGHT_SERVER = True
- FLIGHT_CLIENT = True
-
- JAVA_OPTS = ['-Dio.netty.tryReflectionSetAccessible=true',
- '-Darrow.struct.conflict.policy=CONFLICT_APPEND']
-
- _arrow_version = load_version_from_pom()
- ARROW_TOOLS_JAR = os.environ.get(
- 'ARROW_JAVA_INTEGRATION_JAR',
- os.path.join(ARROW_ROOT_DEFAULT,
- 'java/tools/target/arrow-tools-{}-'
- 'jar-with-dependencies.jar'.format(_arrow_version)))
- ARROW_FLIGHT_JAR = os.environ.get(
- 'ARROW_FLIGHT_JAVA_INTEGRATION_JAR',
- os.path.join(ARROW_ROOT_DEFAULT,
- 'java/flight/flight-core/target/flight-core-{}-'
- 'jar-with-dependencies.jar'.format(_arrow_version)))
- ARROW_FLIGHT_SERVER = ('org.apache.arrow.flight.example.integration.'
- 'IntegrationTestServer')
- ARROW_FLIGHT_CLIENT = ('org.apache.arrow.flight.example.integration.'
- 'IntegrationTestClient')
-
- name = 'Java'
-
- def _run(self, arrow_path=None, json_path=None, command='VALIDATE'):
- cmd = ['java'] + self.JAVA_OPTS + \
- ['-cp', self.ARROW_TOOLS_JAR, 'org.apache.arrow.tools.Integration']
-
- if arrow_path is not None:
- cmd.extend(['-a', arrow_path])
-
- if json_path is not None:
- cmd.extend(['-j', json_path])
-
- cmd.extend(['-c', command])
-
- if self.debug:
- log(' '.join(cmd))
-
- run_cmd(cmd)
-
- def validate(self, json_path, arrow_path):
- return self._run(arrow_path, json_path, 'VALIDATE')
-
- def json_to_file(self, json_path, arrow_path):
- return self._run(arrow_path, json_path, 'JSON_TO_ARROW')
-
- def stream_to_file(self, stream_path, file_path):
- cmd = ['java'] + self.JAVA_OPTS + \
- ['-cp', self.ARROW_TOOLS_JAR,
- 'org.apache.arrow.tools.StreamToFile', stream_path, file_path]
- if self.debug:
- log(' '.join(cmd))
- run_cmd(cmd)
-
- def file_to_stream(self, file_path, stream_path):
- cmd = ['java'] + self.JAVA_OPTS + \
- ['-cp', self.ARROW_TOOLS_JAR,
- 'org.apache.arrow.tools.FileToStream', file_path, stream_path]
- if self.debug:
- log(' '.join(cmd))
- run_cmd(cmd)
-
- def flight_request(self, port, json_path=None, scenario_name=None):
- cmd = ['java'] + self.JAVA_OPTS + \
- ['-cp', self.ARROW_FLIGHT_JAR, self.ARROW_FLIGHT_CLIENT,
- '-port', str(port)]
-
- if json_path:
- cmd.extend(('-j', json_path))
- elif scenario_name:
- cmd.extend(('-scenario', scenario_name))
- else:
- raise TypeError("Must provide one of json_path or scenario_name")
-
- if self.debug:
- log(' '.join(cmd))
- run_cmd(cmd)
-
- @contextlib.contextmanager
- def flight_server(self, scenario_name=None):
- cmd = ['java'] + self.JAVA_OPTS + \
- ['-cp', self.ARROW_FLIGHT_JAR, self.ARROW_FLIGHT_SERVER,
- '-port', '0']
- if scenario_name:
- cmd.extend(('-scenario', scenario_name))
- if self.debug:
- log(' '.join(cmd))
- server = subprocess.Popen(cmd, stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
- try:
- output = server.stdout.readline().decode()
- if not output.startswith("Server listening on localhost:"):
- server.kill()
- out, err = server.communicate()
- raise RuntimeError(
- "Flight-Java server did not start properly, "
- "stdout:\n{}\n\nstderr:\n{}\n"
- .format(output + out.decode(), err.decode()))
- port = int(output.split(":")[1])
- yield port
- finally:
- server.kill()
- server.wait(5)
diff --git a/dev/archery/archery/integration/tester_js.py b/dev/archery/archery/integration/tester_js.py
deleted file mode 100644
index e24eec0..0000000
--- a/dev/archery/archery/integration/tester_js.py
+++ /dev/null
@@ -1,73 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-import os
-
-from .tester import Tester
-from .util import run_cmd, ARROW_ROOT_DEFAULT, log
-
-
-class JSTester(Tester):
- PRODUCER = True
- CONSUMER = True
-
- EXE_PATH = os.path.join(ARROW_ROOT_DEFAULT, 'js/bin')
- VALIDATE = os.path.join(EXE_PATH, 'integration.js')
- JSON_TO_ARROW = os.path.join(EXE_PATH, 'json-to-arrow.js')
- STREAM_TO_FILE = os.path.join(EXE_PATH, 'stream-to-file.js')
- FILE_TO_STREAM = os.path.join(EXE_PATH, 'file-to-stream.js')
-
- name = 'JS'
-
- def _run(self, exe_cmd, arrow_path=None, json_path=None,
- command='VALIDATE'):
- cmd = [exe_cmd]
-
- if arrow_path is not None:
- cmd.extend(['-a', arrow_path])
-
- if json_path is not None:
- cmd.extend(['-j', json_path])
-
- cmd.extend(['--mode', command])
-
- if self.debug:
- log(' '.join(cmd))
-
- run_cmd(cmd)
-
- def validate(self, json_path, arrow_path):
- return self._run(self.VALIDATE, arrow_path, json_path, 'VALIDATE')
-
- def json_to_file(self, json_path, arrow_path):
- cmd = ['node',
- '--no-warnings', self.JSON_TO_ARROW,
- '-a', arrow_path,
- '-j', json_path]
- self.run_shell_command(cmd)
-
- def stream_to_file(self, stream_path, file_path):
- cmd = ['node', '--no-warnings', self.STREAM_TO_FILE,
- '<', stream_path,
- '>', file_path]
- self.run_shell_command(cmd)
-
- def file_to_stream(self, file_path, stream_path):
- cmd = ['node', '--no-warnings', self.FILE_TO_STREAM,
- '<', file_path,
- '>', stream_path]
- self.run_shell_command(cmd)
diff --git a/dev/archery/archery/integration/tester_rust.py b/dev/archery/archery/integration/tester_rust.py
deleted file mode 100644
index bca80eb..0000000
--- a/dev/archery/archery/integration/tester_rust.py
+++ /dev/null
@@ -1,115 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-import contextlib
-import os
-import subprocess
-
-from .tester import Tester
-from .util import run_cmd, ARROW_ROOT_DEFAULT, log
-
-
-class RustTester(Tester):
- PRODUCER = True
- CONSUMER = True
- FLIGHT_SERVER = True
- FLIGHT_CLIENT = True
-
- EXE_PATH = os.path.join(ARROW_ROOT_DEFAULT, 'rust/target/debug')
-
- RUST_INTEGRATION_EXE = os.path.join(EXE_PATH,
- 'arrow-json-integration-test')
- STREAM_TO_FILE = os.path.join(EXE_PATH, 'arrow-stream-to-file')
- FILE_TO_STREAM = os.path.join(EXE_PATH, 'arrow-file-to-stream')
-
- FLIGHT_SERVER_CMD = [
- os.path.join(EXE_PATH, 'flight-test-integration-server')]
- FLIGHT_CLIENT_CMD = [
- os.path.join(EXE_PATH, 'flight-test-integration-client'),
- "--host", "localhost"]
-
- name = 'Rust'
-
- def _run(self, arrow_path=None, json_path=None, command='VALIDATE'):
- cmd = [self.RUST_INTEGRATION_EXE, '--integration']
-
- if arrow_path is not None:
- cmd.append('--arrow=' + arrow_path)
-
- if json_path is not None:
- cmd.append('--json=' + json_path)
-
- cmd.append('--mode=' + command)
-
- if self.debug:
- log(' '.join(cmd))
-
- run_cmd(cmd)
-
- def validate(self, json_path, arrow_path):
- return self._run(arrow_path, json_path, 'VALIDATE')
-
- def json_to_file(self, json_path, arrow_path):
- return self._run(arrow_path, json_path, 'JSON_TO_ARROW')
-
- def stream_to_file(self, stream_path, file_path):
- cmd = [self.STREAM_TO_FILE, '<', stream_path, '>', file_path]
- self.run_shell_command(cmd)
-
- def file_to_stream(self, file_path, stream_path):
- cmd = [self.FILE_TO_STREAM, file_path, '>', stream_path]
- self.run_shell_command(cmd)
-
- @contextlib.contextmanager
- def flight_server(self, scenario_name=None):
- cmd = self.FLIGHT_SERVER_CMD + ['--port=0']
- if scenario_name:
- cmd = cmd + ["--scenario", scenario_name]
- if self.debug:
- log(' '.join(cmd))
- server = subprocess.Popen(cmd,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
- try:
- output = server.stdout.readline().decode()
- if not output.startswith("Server listening on localhost:"):
- server.kill()
- out, err = server.communicate()
- raise RuntimeError(
- "Flight-Rust server did not start properly, "
- "stdout:\n{}\n\nstderr:\n{}\n"
- .format(output + out.decode(), err.decode()))
- port = int(output.split(":")[1])
- yield port
- finally:
- server.kill()
- server.wait(5)
-
- def flight_request(self, port, json_path=None, scenario_name=None):
- cmd = self.FLIGHT_CLIENT_CMD + [
- '--port=' + str(port),
- ]
- if json_path:
- cmd.extend(('--path', json_path))
- elif scenario_name:
- cmd.extend(('--scenario', scenario_name))
- else:
- raise TypeError("Must provide one of json_path or scenario_name")
-
- if self.debug:
- log(' '.join(cmd))
- run_cmd(cmd)
diff --git a/dev/archery/archery/integration/util.py b/dev/archery/archery/integration/util.py
deleted file mode 100644
index a4c4982..0000000
--- a/dev/archery/archery/integration/util.py
+++ /dev/null
@@ -1,166 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-import contextlib
-import io
-import os
-import random
-import socket
-import subprocess
-import sys
-import threading
-import uuid
-
-import numpy as np
-
-
-def guid():
- return uuid.uuid4().hex
-
-
-# SKIP categories
-SKIP_ARROW = 'arrow'
-SKIP_FLIGHT = 'flight'
-
-ARROW_ROOT_DEFAULT = os.environ.get(
- 'ARROW_ROOT',
- os.path.abspath(__file__).rsplit("/", 5)[0]
-)
-
-
-class _Printer:
- """
- A print()-providing object that can override the stream output on
- a per-thread basis.
- """
-
- def __init__(self):
- self._tls = threading.local()
-
- def _get_stdout(self):
- try:
- return self._tls.stdout
- except AttributeError:
- self._tls.stdout = sys.stdout
- self._tls.corked = False
- return self._tls.stdout
-
- def print(self, *args, **kwargs):
- """
- A variant of print() that writes to a thread-local stream.
- """
- print(*args, file=self._get_stdout(), **kwargs)
-
- @property
- def stdout(self):
- """
- A thread-local stdout wrapper that may be temporarily buffered
- using `cork()`.
- """
- return self._get_stdout()
-
- @contextlib.contextmanager
- def cork(self):
- """
- Temporarily buffer this thread's stream and write out its contents
- at the end of the context manager. Useful to avoid interleaved
- output when multiple threads output progress information.
- """
- outer_stdout = self._get_stdout()
- assert not self._tls.corked, "reentrant call"
- inner_stdout = self._tls.stdout = io.StringIO()
- self._tls.corked = True
- try:
- yield
- finally:
- self._tls.stdout = outer_stdout
- self._tls.corked = False
- outer_stdout.write(inner_stdout.getvalue())
- outer_stdout.flush()
-
-
-printer = _Printer()
-log = printer.print
-
-
-_RAND_CHARS = np.array(list("abcdefghijklmnop123456Ârrôwµ£°€矢"), dtype="U")
-
-
-def random_utf8(nchars):
- """
- Generate one random UTF8 string.
- """
- return ''.join(np.random.choice(_RAND_CHARS, nchars))
-
-
-def random_bytes(nbytes):
- """
- Generate one random binary string.
- """
- # NOTE getrandbits(0) fails
- if nbytes > 0:
- return random.getrandbits(nbytes * 8).to_bytes(nbytes,
- byteorder='little')
- else:
- return b""
-
-
-def tobytes(o):
- if isinstance(o, str):
- return o.encode('utf8')
- return o
-
-
-def frombytes(o):
- if isinstance(o, bytes):
- return o.decode('utf8')
- return o
-
-
-def run_cmd(cmd):
- if isinstance(cmd, str):
- cmd = cmd.split(' ')
-
- try:
- output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
- except subprocess.CalledProcessError as e:
- # this avoids hiding the stdout / stderr of failed processes
- sio = io.StringIO()
- print('Command failed:', " ".join(cmd), file=sio)
- print('With output:', file=sio)
- print('--------------', file=sio)
- print(frombytes(e.output), file=sio)
- print('--------------', file=sio)
- raise RuntimeError(sio.getvalue())
-
- return frombytes(output)
-
-
-# Adapted from CPython
-def find_unused_port(family=socket.AF_INET, socktype=socket.SOCK_STREAM):
- """Returns an unused port that should be suitable for binding. This is
- achieved by creating a temporary socket with the same family and type as
- the 'sock' parameter (default is AF_INET, SOCK_STREAM), and binding it to
- the specified host address (defaults to 0.0.0.0) with the port set to 0,
- eliciting an unused ephemeral port from the OS. The temporary socket is
- then closed and deleted, and the ephemeral port is returned.
- """
- with socket.socket(family, socktype) as tempsock:
- tempsock.bind(('', 0))
- port = tempsock.getsockname()[1]
- del tempsock
- return port
diff --git a/dev/archery/archery/lang/__init__.py b/dev/archery/archery/lang/__init__.py
deleted file mode 100644
index 13a8339..0000000
--- a/dev/archery/archery/lang/__init__.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
diff --git a/dev/archery/archery/lang/cpp.py b/dev/archery/archery/lang/cpp.py
deleted file mode 100644
index 045d23b..0000000
--- a/dev/archery/archery/lang/cpp.py
+++ /dev/null
@@ -1,295 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-import os
-
-from ..utils.cmake import CMakeDefinition
-
-
-def truthifier(value):
- return "ON" if value else "OFF"
-
-
-def or_else(value, default):
- return value if value else default
-
-
-def coalesce(value, fallback):
- return fallback if value is None else value
-
-
-LLVM_VERSION = 7
-
-
-class CppConfiguration:
- def __init__(self,
-
- # toolchain
- cc=None, cxx=None, cxx_flags=None,
- build_type=None, warn_level=None,
- cpp_package_prefix=None, install_prefix=None, use_conda=None,
- build_static=False, build_shared=True,
- # tests & examples
- with_tests=None, with_benchmarks=None, with_examples=None,
- with_integration=None,
- # static checks
- use_asan=None, use_tsan=None, use_ubsan=None,
- with_fuzzing=None,
- # Components
- with_compute=None, with_csv=None, with_cuda=None,
- with_dataset=None, with_filesystem=None, with_flight=None,
- with_gandiva=None, with_hdfs=None, with_hiveserver2=None,
- with_ipc=True, with_json=None, with_jni=None,
- with_mimalloc=None,
- with_parquet=None, with_plasma=None, with_python=True,
- with_r=None, with_s3=None,
- # Compressions
- with_brotli=None, with_bz2=None, with_lz4=None,
- with_snappy=None, with_zlib=None, with_zstd=None,
- # extras
- with_lint_only=False,
- use_gold_linker=True,
- simd_level="SSE4_2",
- cmake_extras=None):
- self._cc = cc
- self._cxx = cxx
- self.cxx_flags = cxx_flags
-
- self._build_type = build_type
- self.warn_level = warn_level
- self._install_prefix = install_prefix
- self._package_prefix = cpp_package_prefix
- self._use_conda = use_conda
- self.build_static = build_static
- self.build_shared = build_shared
-
- self.with_tests = with_tests
- self.with_benchmarks = with_benchmarks
- self.with_examples = with_examples
- self.with_integration = with_integration
-
- self.use_asan = use_asan
- self.use_tsan = use_tsan
- self.use_ubsan = use_ubsan
- self.with_fuzzing = with_fuzzing
-
- self.with_compute = with_compute
- self.with_csv = with_csv
- self.with_cuda = with_cuda
- self.with_dataset = with_dataset
- self.with_filesystem = with_filesystem
- self.with_flight = with_flight
- self.with_gandiva = with_gandiva
- self.with_hdfs = with_hdfs
- self.with_hiveserver2 = with_hiveserver2
- self.with_ipc = with_ipc
- self.with_json = with_json
- self.with_jni = with_jni
- self.with_mimalloc = with_mimalloc
- self.with_parquet = with_parquet
- self.with_plasma = with_plasma
- self.with_python = with_python
- self.with_r = with_r
- self.with_s3 = with_s3
-
- self.with_brotli = with_brotli
- self.with_bz2 = with_bz2
- self.with_lz4 = with_lz4
- self.with_snappy = with_snappy
- self.with_zlib = with_zlib
- self.with_zstd = with_zstd
-
- self.with_lint_only = with_lint_only
- self.use_gold_linker = use_gold_linker
- self.simd_level = simd_level
-
- self.cmake_extras = cmake_extras
-
- # Fixup required dependencies by providing sane defaults if the caller
- # didn't specify the option.
- if self.with_r:
- self.with_csv = coalesce(with_csv, True)
- self.with_dataset = coalesce(with_dataset, True)
- self.with_filesystem = coalesce(with_filesystem, True)
- self.with_ipc = coalesce(with_ipc, True)
- self.with_json = coalesce(with_json, True)
- self.with_parquet = coalesce(with_parquet, True)
-
- if self.with_python:
- self.with_zlib = coalesce(with_zlib, True)
- self.with_lz4 = coalesce(with_lz4, True)
-
- if self.with_dataset:
- self.with_filesystem = coalesce(with_filesystem, True)
- self.with_parquet = coalesce(with_parquet, True)
-
- if self.with_parquet:
- self.with_snappy = coalesce(with_snappy, True)
-
- @property
- def build_type(self):
- if self._build_type:
- return self._build_type
-
- if self.with_fuzzing:
- return "relwithdebinfo"
-
- return "release"
-
- @property
- def cc(self):
- if self._cc:
- return self._cc
-
- if self.with_fuzzing:
- return "clang-{}".format(LLVM_VERSION)
-
- return None
-
- @property
- def cxx(self):
- if self._cxx:
- return self._cxx
-
- if self.with_fuzzing:
- return "clang++-{}".format(LLVM_VERSION)
-
- return None
-
- def _gen_defs(self):
- if self.cxx_flags:
- yield ("ARROW_CXXFLAGS", self.cxx_flags)
-
- yield ("CMAKE_EXPORT_COMPILE_COMMANDS", truthifier(True))
- yield ("CMAKE_BUILD_TYPE", self.build_type)
- yield ("CMAKE_UNITY_BUILD", True)
-
- if not self.with_lint_only:
- yield ("BUILD_WARNING_LEVEL",
- or_else(self.warn_level, "production"))
-
- # if not ctx.quiet:
- # yield ("ARROW_VERBOSE_THIRDPARTY_BUILD", "ON")
-
- maybe_prefix = self.install_prefix
- if maybe_prefix:
- yield ("CMAKE_INSTALL_PREFIX", maybe_prefix)
-
- if self._package_prefix is not None:
- yield ("ARROW_DEPENDENCY_SOURCE", "SYSTEM")
- yield ("ARROW_PACKAGE_PREFIX", self._package_prefix)
-
- yield ("ARROW_BUILD_STATIC", truthifier(self.build_static))
- yield ("ARROW_BUILD_SHARED", truthifier(self.build_shared))
-
- # Tests and benchmarks
- yield ("ARROW_BUILD_TESTS", truthifier(self.with_tests))
- yield ("ARROW_BUILD_BENCHMARKS", truthifier(self.with_benchmarks))
- yield ("ARROW_BUILD_EXAMPLES", truthifier(self.with_examples))
- yield ("ARROW_BUILD_INTEGRATION", truthifier(self.with_integration))
-
- # Static checks
- yield ("ARROW_USE_ASAN", truthifier(self.use_asan))
- yield ("ARROW_USE_TSAN", truthifier(self.use_tsan))
- yield ("ARROW_USE_UBSAN", truthifier(self.use_ubsan))
- yield ("ARROW_FUZZING", truthifier(self.with_fuzzing))
-
- # Components
- yield ("ARROW_COMPUTE", truthifier(self.with_compute))
- yield ("ARROW_CSV", truthifier(self.with_csv))
- yield ("ARROW_CUDA", truthifier(self.with_cuda))
- yield ("ARROW_DATASET", truthifier(self.with_dataset))
- yield ("ARROW_FILESYSTEM", truthifier(self.with_filesystem))
- yield ("ARROW_FLIGHT", truthifier(self.with_flight))
- yield ("ARROW_GANDIVA", truthifier(self.with_gandiva))
- yield ("ARROW_PARQUET", truthifier(self.with_parquet))
- yield ("ARROW_HDFS", truthifier(self.with_hdfs))
- yield ("ARROW_HIVESERVER2", truthifier(self.with_hiveserver2))
- yield ("ARROW_IPC", truthifier(self.with_ipc))
- yield ("ARROW_JSON", truthifier(self.with_json))
- yield ("ARROW_JNI", truthifier(self.with_jni))
- yield ("ARROW_MIMALLOC", truthifier(self.with_mimalloc))
- yield ("ARROW_PLASMA", truthifier(self.with_plasma))
- yield ("ARROW_PYTHON", truthifier(self.with_python))
- yield ("ARROW_S3", truthifier(self.with_s3))
-
- # Compressions
- yield ("ARROW_WITH_BROTLI", truthifier(self.with_brotli))
- yield ("ARROW_WITH_BZ2", truthifier(self.with_bz2))
- yield ("ARROW_WITH_LZ4", truthifier(self.with_lz4))
- yield ("ARROW_WITH_SNAPPY", truthifier(self.with_snappy))
- yield ("ARROW_WITH_ZLIB", truthifier(self.with_zlib))
- yield ("ARROW_WITH_ZSTD", truthifier(self.with_zstd))
-
- yield ("ARROW_LINT_ONLY", truthifier(self.with_lint_only))
-
- # Some configurations don't like gnu gold linker.
- broken_with_gold_ld = [self.with_fuzzing, self.with_gandiva]
- if self.use_gold_linker and not any(broken_with_gold_ld):
- yield ("ARROW_USE_LD_GOLD", truthifier(self.use_gold_linker))
- yield ("ARROW_SIMD_LEVEL", or_else(self.simd_level, "SSE4_2"))
-
- # Detect custom conda toolchain
- if self.use_conda:
- for d, v in [('CMAKE_AR', 'AR'), ('CMAKE_RANLIB', 'RANLIB')]:
- v = os.environ.get(v)
- if v:
- yield (d, v)
-
- @property
- def install_prefix(self):
- if self._install_prefix:
- return self._install_prefix
-
- if self.use_conda:
- return os.environ.get("CONDA_PREFIX")
-
- return None
-
- @property
- def use_conda(self):
- # If the user didn't specify a preference, guess via environment
- if self._use_conda is None:
- return os.environ.get("CONDA_PREFIX") is not None
-
- return self._use_conda
-
- @property
- def definitions(self):
- extras = list(self.cmake_extras) if self.cmake_extras else []
- definitions = ["-D{}={}".format(d[0], d[1]) for d in self._gen_defs()]
- return definitions + extras
-
- @property
- def environment(self):
- env = os.environ.copy()
-
- if self.cc:
- env["CC"] = self.cc
-
- if self.cxx:
- env["CXX"] = self.cxx
-
- return env
-
-
-class CppCMakeDefinition(CMakeDefinition):
- def __init__(self, source, conf, **kwargs):
- self.configuration = conf
- super().__init__(source, **kwargs,
- definitions=conf.definitions, env=conf.environment,
- build_type=conf.build_type)
diff --git a/dev/archery/archery/lang/java.py b/dev/archery/archery/lang/java.py
deleted file mode 100644
index 24743b6..0000000
--- a/dev/archery/archery/lang/java.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-from ..utils.command import Command, CommandStackMixin, default_bin
-
-
-class Java(Command):
- def __init__(self, java_bin=None):
- self.bin = default_bin(java_bin, "java")
-
-
-class Jar(CommandStackMixin, Java):
- def __init__(self, jar, *args, **kwargs):
- self.jar = jar
- self.argv = ("-jar", jar)
- Java.__init__(self, *args, **kwargs)
diff --git a/dev/archery/archery/lang/python.py b/dev/archery/archery/lang/python.py
deleted file mode 100644
index 4952d5f..0000000
--- a/dev/archery/archery/lang/python.py
+++ /dev/null
@@ -1,218 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-import inspect
-import tokenize
-from contextlib import contextmanager
-
-try:
- from numpydoc.validate import Docstring, validate
-except ImportError:
- have_numpydoc = False
-else:
- have_numpydoc = True
-
-from ..utils.command import Command, capture_stdout, default_bin
-
-
-class Flake8(Command):
- def __init__(self, flake8_bin=None):
- self.bin = default_bin(flake8_bin, "flake8")
-
-
-class Autopep8(Command):
- def __init__(self, autopep8_bin=None):
- self.bin = default_bin(autopep8_bin, "autopep8")
-
- @capture_stdout()
- def run_captured(self, *args, **kwargs):
- return self.run(*args, **kwargs)
-
-
-def _tokenize_signature(s):
- lines = s.encode('ascii').splitlines()
- generator = iter(lines).__next__
- return tokenize.tokenize(generator)
-
-
-def _convert_typehint(tokens):
- names = []
- opening_bracket_reached = False
- for token in tokens:
- # omit the tokens before the opening bracket
- if not opening_bracket_reached:
- if token.string == '(':
- opening_bracket_reached = True
- else:
- continue
-
- if token.type == 1: # type 1 means NAME token
- names.append(token)
- else:
- if len(names) == 1:
- yield (names[0].type, names[0].string)
- elif len(names) == 2:
- # two "NAME" tokens follow each other which means a cython
- # typehint like `bool argument`, so remove the typehint
- # note that we could convert it to python typehints, but hints
- # are not supported by _signature_fromstr
- yield (names[1].type, names[1].string)
- elif len(names) > 2:
- raise ValueError('More than two NAME tokens follow each other')
- names = []
- yield (token.type, token.string)
-
-
-def inspect_signature(obj):
- """
- Custom signature inspection primarily for cython generated callables.
-
- Cython puts the signatures to the first line of the docstrings, which we
- can reuse to parse the python signature from, but some gymnastics are
- required, like removing the cython typehints.
-
- It converts the cython signature:
- array(obj, type=None, mask=None, size=None, from_pandas=None,
- bool safe=True, MemoryPool memory_pool=None)
- To:
- <Signature (obj, type=None, mask=None, size=None, from_pandas=None,
- safe=True, memory_pool=None)>
- """
- cython_signature = obj.__doc__.splitlines()[0]
- cython_tokens = _tokenize_signature(cython_signature)
- python_tokens = _convert_typehint(cython_tokens)
- python_signature = tokenize.untokenize(python_tokens)
- return inspect._signature_fromstr(inspect.Signature, obj, python_signature)
-
-
-class NumpyDoc:
-
- def __init__(self, symbols=None):
- if not have_numpydoc:
- raise RuntimeError(
- 'Numpydoc is not available, install the development version '
- 'with command: pip install '
- 'git+https://github.com/numpy/numpydoc'
- )
- self.symbols = set(symbols or {'pyarrow'})
-
- def traverse(self, fn, obj, from_package):
- """Apply a function on publicly exposed API components.
-
- Recursively iterates over the members of the passed object. It omits
- any '_' prefixed and thirdparty (non pyarrow) symbols.
-
- Parameters
- ----------
- obj : Any
- from_package : string, default 'pyarrow'
- Predicate to only consider objects from this package.
- """
- todo = [obj]
- seen = set()
-
- while todo:
- obj = todo.pop()
- if obj in seen:
- continue
- else:
- seen.add(obj)
-
- fn(obj)
-
- for name in dir(obj):
- if name.startswith('_'):
- continue
-
- member = getattr(obj, name)
- module = getattr(member, '__module__', None)
- if not (module and module.startswith(from_package)):
- continue
-
- todo.append(member)
-
- @contextmanager
- def _apply_patches(self):
- """
- Patch Docstring class to bypass loading already loaded python objects.
- """
- orig_load_obj = Docstring._load_obj
- orig_signature = inspect.signature
-
- @staticmethod
- def _load_obj(obj):
- # By default it expects a qualname and import the object, but we
- # have already loaded object after the API traversal.
- if isinstance(obj, str):
- return orig_load_obj(obj)
- else:
- return obj
-
- def signature(obj):
- # inspect.signature tries to parse __text_signature__ if other
- # properties like __signature__ doesn't exists, but cython
- # doesn't set that property despite that embedsignature cython
- # directive is set. The only way to inspect a cython compiled
- # callable's signature to parse it from __doc__ while
- # embedsignature directive is set during the build phase.
- # So path inspect.signature function to attempt to parse the first
- # line of callable.__doc__ as a signature.
- try:
- return orig_signature(obj)
- except Exception as orig_error:
- try:
- return inspect_signature(obj)
- except Exception:
- raise orig_error
-
- try:
- Docstring._load_obj = _load_obj
- inspect.signature = signature
- yield
- finally:
- Docstring._load_obj = orig_load_obj
- inspect.signature = orig_signature
-
- def validate(self, from_package='', allow_rules=None,
- disallow_rules=None):
- results = []
-
- def callback(obj):
- result = validate(obj)
-
- errors = []
- for errcode, errmsg in result.get('errors', []):
- if allow_rules and errcode not in allow_rules:
- continue
- if disallow_rules and errcode in disallow_rules:
- continue
- errors.append((errcode, errmsg))
-
- if len(errors):
- result['errors'] = errors
- results.append((obj, result))
-
- with self._apply_patches():
- for symbol in self.symbols:
- try:
- obj = Docstring._load_obj(symbol)
- except (ImportError, AttributeError):
- print('{} is not available for import'.format(symbol))
- else:
- self.traverse(callback, obj, from_package=from_package)
-
- return results
diff --git a/dev/archery/archery/lang/rust.py b/dev/archery/archery/lang/rust.py
deleted file mode 100644
index b1d765b..0000000
--- a/dev/archery/archery/lang/rust.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-from ..utils.command import Command, default_bin
-
-
-class Cargo(Command):
- def __init__(self, cargo_bin=None):
- self.bin = default_bin(cargo_bin, "cargo")
diff --git a/dev/archery/archery/release.py b/dev/archery/archery/release.py
deleted file mode 100644
index acfe3fc..0000000
--- a/dev/archery/archery/release.py
+++ /dev/null
@@ -1,535 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-from collections import defaultdict
-import functools
-import os
-import re
-import pathlib
-import shelve
-import warnings
-
-from git import Repo
-from jira import JIRA
-from semver import VersionInfo as SemVer
-
-from .utils.source import ArrowSources
-from .utils.report import JinjaReport
-
-
-def cached_property(fn):
- return property(functools.lru_cache(maxsize=1)(fn))
-
-
-class Version(SemVer):
-
- __slots__ = ('released', 'release_date')
-
- def __init__(self, released=False, release_date=None, **kwargs):
- super().__init__(**kwargs)
- self.released = released
- self.release_date = release_date
-
- @classmethod
- def parse(cls, version, **kwargs):
- return cls(**SemVer.parse(version).to_dict(), **kwargs)
-
- @classmethod
- def from_jira(cls, jira_version):
- return cls.parse(
- jira_version.name,
- released=jira_version.released,
- release_date=getattr(jira_version, 'releaseDate', None)
- )
-
-
-class Issue:
-
- def __init__(self, key, type, summary):
- self.key = key
- self.type = type
- self.summary = summary
-
- @classmethod
- def from_jira(cls, jira_issue):
- return cls(
- key=jira_issue.key,
- type=jira_issue.fields.issuetype.name,
- summary=jira_issue.fields.summary
- )
-
- @property
- def project(self):
- return self.key.split('-')[0]
-
- @property
- def number(self):
- return int(self.key.split('-')[1])
-
-
-class Jira(JIRA):
-
- def __init__(self, user=None, password=None,
- url='https://issues.apache.org/jira'):
- user = user or os.environ.get('APACHE_JIRA_USER')
- password = password or os.environ.get('APACHE_JIRA_PASSWORD')
- super().__init__(url, basic_auth=(user, password))
-
- def project_version(self, version_string, project='ARROW'):
- # query version from jira to populated with additional metadata
- versions = {str(v): v for v in self.project_versions(project)}
- return versions[version_string]
-
- def project_versions(self, project):
- versions = []
- for v in super().project_versions(project):
- try:
- versions.append(Version.from_jira(v))
- except ValueError:
- # ignore invalid semantic versions like JS-0.4.0
- continue
- return sorted(versions, reverse=True)
-
- def issue(self, key):
- return Issue.from_jira(super().issue(key))
-
- def project_issues(self, version, project='ARROW'):
- query = "project={} AND fixVersion={}".format(project, version)
- issues = super().search_issues(query, maxResults=False)
- return list(map(Issue.from_jira, issues))
-
-
-class CachedJira:
-
- def __init__(self, cache_path, jira=None):
- self.jira = jira or Jira()
- self.cache_path = cache_path
-
- def __getattr__(self, name):
- attr = getattr(self.jira, name)
- return self._cached(name, attr) if callable(attr) else attr
-
- def _cached(self, name, method):
- def wrapper(*args, **kwargs):
- key = str((name, args, kwargs))
- with shelve.open(self.cache_path) as cache:
- try:
- result = cache[key]
- except KeyError:
- cache[key] = result = method(*args, **kwargs)
- return result
- return wrapper
-
-
-_TITLE_REGEX = re.compile(
- r"(?P<issue>(?P<project>(ARROW|PARQUET))\-\d+)?\s*:?\s*"
- r"(?P<components>\[.*\])?\s*(?P<summary>.*)"
-)
-_COMPONENT_REGEX = re.compile(r"\[([^\[\]]+)\]")
-
-
-class CommitTitle:
-
- def __init__(self, summary, project=None, issue=None, components=None):
- self.project = project
- self.issue = issue
- self.components = components or []
- self.summary = summary
-
- def __str__(self):
- out = ""
- if self.issue:
- out += "{}: ".format(self.issue)
- if self.components:
- for component in self.components:
- out += "[{}]".format(component)
- out += " "
- out += self.summary
- return out
-
- def __eq__(self, other):
- return (
- self.summary == other.summary and
- self.project == other.project and
- self.issue == other.issue and
- self.components == other.components
- )
-
- def __hash__(self):
- return hash(
- (self.summary, self.project, self.issue, tuple(self.components))
- )
-
- @classmethod
- def parse(cls, headline):
- matches = _TITLE_REGEX.match(headline)
- if matches is None:
- warnings.warn(
- "Unable to parse commit message `{}`".format(headline)
- )
- return CommitTitle(headline)
-
- values = matches.groupdict()
- components = values.get('components') or ''
- components = _COMPONENT_REGEX.findall(components)
-
- return CommitTitle(
- values['summary'],
- project=values.get('project'),
- issue=values.get('issue'),
- components=components
- )
-
-
-class Commit:
-
- def __init__(self, wrapped):
- self._title = CommitTitle.parse(wrapped.summary)
- self._wrapped = wrapped
-
- def __getattr__(self, attr):
- if hasattr(self._title, attr):
- return getattr(self._title, attr)
- else:
- return getattr(self._wrapped, attr)
-
- def __repr__(self):
- template = '<Commit sha={!r} issue={!r} components={!r} summary={!r}>'
- return template.format(self.hexsha, self.issue, self.components,
- self.summary)
-
- @property
- def url(self):
- return 'https://github.com/apache/arrow/commit/{}'.format(self.hexsha)
-
- @property
- def title(self):
- return self._title
-
-
-class ReleaseCuration(JinjaReport):
- templates = {
- 'console': 'release_curation.txt.j2'
- }
- fields = [
- 'release',
- 'within',
- 'outside',
- 'nojira',
- 'parquet',
- 'nopatch'
- ]
-
-
-class JiraChangelog(JinjaReport):
- templates = {
- 'markdown': 'release_changelog.md.j2',
- 'html': 'release_changelog.html.j2'
- }
- fields = [
- 'release',
- 'categories'
- ]
-
-
-class Release:
-
- def __init__(self):
- raise TypeError("Do not initialize Release class directly, use "
- "Release.from_jira(version) instead.")
-
- def __repr__(self):
- if self.version.released:
- status = "released_at={!r}".format(self.version.release_date)
- else:
- status = "pending"
- return "<{} {!r} {}>".format(self.__class__.__name__,
- str(self.version), status)
-
- @staticmethod
- def from_jira(version, jira=None, repo=None):
- if jira is None:
- jira = Jira()
- elif isinstance(jira, str):
- jira = Jira(jira)
- elif not isinstance(jira, (Jira, CachedJira)):
- raise TypeError("`jira` argument must be a server url or a valid "
- "Jira instance")
-
- if repo is None:
- arrow = ArrowSources.find()
- repo = Repo(arrow.path)
- elif isinstance(repo, (str, pathlib.Path)):
- repo = Repo(repo)
- elif not isinstance(repo, Repo):
- raise TypeError("`repo` argument must be a path or a valid Repo "
- "instance")
-
- if isinstance(version, str):
- version = jira.project_version(version, project='ARROW')
- elif not isinstance(version, Version):
- raise TypeError(version)
-
- # decide the type of the release based on the version number
- if version.patch == 0:
- if version.minor == 0:
- klass = MajorRelease
- elif version.major == 0:
- # handle minor releases before 1.0 as major releases
- klass = MajorRelease
- else:
- klass = MinorRelease
- else:
- klass = PatchRelease
-
- # prevent instantiating release object directly
- obj = klass.__new__(klass)
- obj.version = version
- obj.jira = jira
- obj.repo = repo
-
- return obj
-
- @property
- def is_released(self):
- return self.version.released
-
- @property
- def tag(self):
- return "apache-arrow-{}".format(str(self.version))
-
- @property
- def branch(self):
- raise NotImplementedError()
-
- @property
- def siblings(self):
- """
- Releases to consider when calculating previous and next releases.
- """
- raise NotImplementedError()
-
- @cached_property
- def previous(self):
- # select all non-patch releases
- position = self.siblings.index(self.version)
- try:
- previous = self.siblings[position + 1]
- except IndexError:
- # first release doesn't have a previous one
- return None
- else:
- return Release.from_jira(previous, jira=self.jira, repo=self.repo)
-
- @cached_property
- def next(self):
- # select all non-patch releases
- position = self.siblings.index(self.version)
- if position <= 0:
- raise ValueError("There is no upcoming release set in JIRA after "
- "version {}".format(self.version))
- upcoming = self.siblings[position - 1]
- return Release.from_jira(upcoming, jira=self.jira, repo=self.repo)
-
- @cached_property
- def issues(self):
- issues = self.jira.project_issues(self.version, project='ARROW')
- return {i.key: i for i in issues}
-
- @cached_property
- def commits(self):
- """
- All commits applied between two versions.
- """
- if self.previous is None:
- # first release
- lower = ''
- else:
- lower = self.repo.tags[self.previous.tag]
-
- if self.version.released:
- upper = self.repo.tags[self.tag]
- else:
- try:
- upper = self.repo.branches[self.branch]
- except IndexError:
- warnings.warn("Release branch `{}` doesn't exist."
- .format(self.branch))
- return []
-
- commit_range = "{}..{}".format(lower, upper)
- return list(map(Commit, self.repo.iter_commits(commit_range)))
-
- def curate(self):
- # handle commits with parquet issue key specially and query them from
- # jira and add it to the issues
- release_issues = self.issues
-
- within, outside, nojira, parquet = [], [], [], []
- for c in self.commits:
- if c.issue is None:
- nojira.append(c)
- elif c.issue in release_issues:
- within.append((release_issues[c.issue], c))
- elif c.project == 'PARQUET':
- parquet.append((self.jira.issue(c.issue), c))
- else:
- outside.append((self.jira.issue(c.issue), c))
-
- # remaining jira tickets
- within_keys = {i.key for i, c in within}
- nopatch = [issue for key, issue in release_issues.items()
- if key not in within_keys]
-
- return ReleaseCuration(release=self, within=within, outside=outside,
- nojira=nojira, parquet=parquet, nopatch=nopatch)
-
- def changelog(self):
- release_issues = []
-
- # get organized report for the release
- curation = self.curate()
-
- # jira tickets having patches in the release
- for issue, _ in curation.within:
- release_issues.append(issue)
-
- # jira tickets without patches
- for issue in curation.nopatch:
- release_issues.append(issue)
-
- # parquet patches in the release
- for issue, _ in curation.parquet:
- release_issues.append(issue)
-
- # organize issues into categories
- issue_types = {
- 'Bug': 'Bug Fixes',
- 'Improvement': 'New Features and Improvements',
- 'New Feature': 'New Features and Improvements',
- 'Sub-task': 'New Features and Improvements',
- 'Task': 'New Features and Improvements',
- 'Test': 'Bug Fixes',
- 'Wish': 'New Features and Improvements',
- }
- categories = defaultdict(list)
- for issue in release_issues:
- categories[issue_types[issue.type]].append(issue)
-
- # sort issues by the issue key in ascending order
- for name, issues in categories.items():
- issues.sort(key=lambda issue: (issue.project, issue.number))
-
- return JiraChangelog(release=self, categories=categories)
-
-
-class MaintenanceMixin:
- """
- Utility methods for cherry-picking commits from the main branch.
- """
-
- def commits_to_pick(self, exclude_already_applied=True):
- # collect commits applied on the main branch since the root of the
- # maintenance branch (the previous major release)
- if self.version.major == 0:
- # treat minor releases as major releases preceeding 1.0.0 release
- commit_range = "apache-arrow-0.{}.0..master".format(
- self.version.minor - 1
- )
- else:
- commit_range = "apache-arrow-{}.0.0..master".format(
- self.version.major
- )
-
- # keeping the original order of the commits helps to minimize the merge
- # conflicts during cherry-picks
- commits = map(Commit, self.repo.iter_commits(commit_range))
-
- # exclude patches that have been already applied to the maintenance
- # branch, we cannot identify patches based on sha because it changes
- # after the cherry pick so use commit title instead
- if exclude_already_applied:
- already_applied = {c.title for c in self.commits}
- else:
- already_applied = set()
-
- # iterate over the commits applied on the main branch and filter out
- # the ones that are included in the jira release
- patches_to_pick = [c for c in commits if
- c.issue in self.issues and
- c.title not in already_applied]
-
- return reversed(patches_to_pick)
-
- def cherry_pick_commits(self, recreate_branch=True):
- if recreate_branch:
- # delete, create and checkout the maintenance branch based off of
- # the previous tag
- if self.branch in self.repo.branches:
- self.repo.git.branch('-D', self.branch)
- self.repo.git.checkout(self.previous.tag, b=self.branch)
- else:
- # just checkout the already existing maintenance branch
- self.repo.git.checkout(self.branch)
-
- # cherry pick the commits based on the jira tickets
- for commit in self.commits_to_pick():
- self.repo.git.cherry_pick(commit.hexsha)
-
-
-class MajorRelease(Release):
-
- @property
- def branch(self):
- return "master"
-
- @cached_property
- def siblings(self):
- """
- Filter only the major releases.
- """
- # handle minor releases before 1.0 as major releases
- return [v for v in self.jira.project_versions('ARROW')
- if v.patch == 0 and (v.major == 0 or v.minor == 0)]
-
-
-class MinorRelease(Release, MaintenanceMixin):
-
- @property
- def branch(self):
- return "maint-{}.x.x".format(self.version.major)
-
- @cached_property
- def siblings(self):
- """
- Filter the major and minor releases.
- """
- return [v for v in self.jira.project_versions('ARROW') if v.patch == 0]
-
-
-class PatchRelease(Release, MaintenanceMixin):
-
- @property
- def branch(self):
- return "maint-{}.{}.x".format(self.version.major, self.version.minor)
-
- @cached_property
- def siblings(self):
- """
- No filtering, consider all releases.
- """
- return self.jira.project_versions('ARROW')
diff --git a/dev/archery/archery/templates/release_changelog.md.j2 b/dev/archery/archery/templates/release_changelog.md.j2
deleted file mode 100644
index c0406dd..0000000
--- a/dev/archery/archery/templates/release_changelog.md.j2
+++ /dev/null
@@ -1,29 +0,0 @@
-{#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#}
-# Apache Arrow {{ release.version }} ({{ release.version.release_date or today() }})
-
-{% for category, issues in categories.items() -%}
-
-## {{ category }}
-
-{% for issue in issues -%}
-* [{{ issue.key }}](https://issues.apache.org/jira/browse/{{ issue.key }}) - {{ issue.summary | md }}
-{% endfor %}
-
-{% endfor %}
diff --git a/dev/archery/archery/templates/release_curation.txt.j2 b/dev/archery/archery/templates/release_curation.txt.j2
deleted file mode 100644
index a5d11e9..0000000
--- a/dev/archery/archery/templates/release_curation.txt.j2
+++ /dev/null
@@ -1,41 +0,0 @@
-{#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#}
-Total number of JIRA tickets assigned to version {{ release.version }}: {{ release.issues|length }}
-
-Total number of applied patches since version {{ release.previous.version }}: {{ release.commits|length }}
-
-Patches with assigned issue in version {{ release.version }}:
-{% for issue, commit in within -%}
- - {{ commit.url }} {{ commit.title }}
-{% endfor %}
-
-Patches with assigned issue outside of version {{ release.version }}:
-{% for issue, commit in outside -%}
- - {{ commit.url }} {{ commit.title }}
-{% endfor %}
-
-Patches in version {{ release.version }} without a linked issue:
-{% for commit in nojira -%}
- - {{ commit.url }} {{ commit.title }}
-{% endfor %}
-
-JIRA issues in version {{ release.version }} without a linked patch:
-{% for issue in nopatch -%}
- - https://issues.apache.org/jira/browse/{{ issue.key }}
-{% endfor %}
diff --git a/dev/archery/archery/testing.py b/dev/archery/archery/testing.py
deleted file mode 100644
index 471a54d..0000000
--- a/dev/archery/archery/testing.py
+++ /dev/null
@@ -1,83 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-from contextlib import contextmanager
-import os
-from unittest import mock
-import re
-
-
-class DotDict(dict):
-
- def __getattr__(self, key):
- try:
- item = self[key]
- except KeyError:
- raise AttributeError(key)
- if isinstance(item, dict):
- return DotDict(item)
- else:
- return item
-
-
-class PartialEnv(dict):
-
- def __eq__(self, other):
- return self.items() <= other.items()
-
-
-_mock_call_type = type(mock.call())
-
-
-def _ensure_mock_call_object(obj, **kwargs):
- if isinstance(obj, _mock_call_type):
- return obj
- elif isinstance(obj, str):
- cmd = re.split(r"\s+", obj)
- return mock.call(cmd, **kwargs)
- elif isinstance(obj, list):
- return mock.call(obj, **kwargs)
- else:
- raise TypeError(obj)
-
-
-class SuccessfulSubprocessResult:
-
- def check_returncode(self):
- return
-
-
-@contextmanager
-def assert_subprocess_calls(expected_commands_or_calls, **kwargs):
- calls = [
- _ensure_mock_call_object(obj, **kwargs)
- for obj in expected_commands_or_calls
- ]
- with mock.patch('subprocess.run', autospec=True) as run:
- run.return_value = SuccessfulSubprocessResult()
- yield run
- run.assert_has_calls(calls)
-
-
-@contextmanager
-def override_env(mapping):
- original = os.environ
- try:
- os.environ = dict(os.environ, **mapping)
- yield os.environ
- finally:
- os.environ = original
diff --git a/dev/archery/archery/tests/fixtures/archery-benchmark-diff-empty-lines.jsonl b/dev/archery/archery/tests/fixtures/archery-benchmark-diff-empty-lines.jsonl
deleted file mode 100644
index 5854eb7..0000000
--- a/dev/archery/archery/tests/fixtures/archery-benchmark-diff-empty-lines.jsonl
+++ /dev/null
@@ -1,6 +0,0 @@
-{"benchmark": "RegressionSumKernel/32768/10", "change": 0.0046756468886368545, "regression": false, "baseline": 13265442258.099466, "contender": 13327466781.91994, "unit": "bytes_per_second", "less_is_better": false, "suite": "arrow-compute-aggregate-benchmark"}
-{"benchmark": "RegressionSumKernel/32768/1", "change": 0.0025108399115900733, "regression": false, "baseline": 15181891659.539782, "contender": 15220010959.05199, "unit": "bytes_per_second", "less_is_better": false, "suite": "arrow-compute-aggregate-benchmark"}
-
-{"benchmark": "RegressionSumKernel/32768/50", "change": 0.00346735806287155, "regression": false, "baseline": 11471825667.817123, "contender": 11511602595.042286, "unit": "bytes_per_second", "less_is_better": false, "suite": "arrow-compute-aggregate-benchmark"}
-
-{"benchmark": "RegressionSumKernel/32768/0", "change": 0.010140954727954987, "regression": false, "baseline": 18316987019.994465, "contender": 18502738756.116768, "unit": "bytes_per_second", "less_is_better": false, "suite": "arrow-compute-aggregate-benchmark"}
diff --git a/dev/archery/archery/tests/fixtures/archery-benchmark-diff.jsonl b/dev/archery/archery/tests/fixtures/archery-benchmark-diff.jsonl
deleted file mode 100644
index 1e25810..0000000
--- a/dev/archery/archery/tests/fixtures/archery-benchmark-diff.jsonl
+++ /dev/null
@@ -1,4 +0,0 @@
-{"benchmark":"RegressionSumKernel/32768/50","change":-0.001550846227215492,"regression":false,"baseline":19241207435.428757,"contender":19211367281.47045,"unit":"bytes_per_second","less_is_better":false,"suite":"arrow-compute-aggregate-benchmark"}
-{"benchmark":"RegressionSumKernel/32768/1","change":0.0020681767923465765,"regression":true,"baseline":24823170673.777943,"contender":24771831968.277977,"unit":"bytes_per_second","less_is_better":false,"suite":"arrow-compute-aggregate-benchmark"}
-{"benchmark":"RegressionSumKernel/32768/10","change":0.0033323376378746905,"regression":false,"baseline":21902707565.968014,"contender":21975694782.76145,"unit":"bytes_per_second","less_is_better":false,"suite":"arrow-compute-aggregate-benchmark"}
-{"benchmark":"RegressionSumKernel/32768/0","change":-0.004918126090954414,"regression":true,"baseline":27685006611.446762,"contender":27821164964.790764,"unit":"bytes_per_second","less_is_better":false,"suite":"arrow-compute-aggregate-benchmark"}
diff --git a/dev/archery/archery/tests/fixtures/event-issue-comment-build-command.json b/dev/archery/archery/tests/fixtures/event-issue-comment-build-command.json
deleted file mode 100644
index d591105..0000000
--- a/dev/archery/archery/tests/fixtures/event-issue-comment-build-command.json
+++ /dev/null
@@ -1,212 +0,0 @@
-{
- "action": "created",
- "comment": {
- "author_association": "MEMBER",
- "body": "@ursabot build",
- "created_at": "2019-04-05T11:55:43Z",
- "html_url": "https://github.com/ursa-labs/ursabot/pull/26#issuecomment-480248726",
- "id": 480248726,
- "issue_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/26",
- "node_id": "MDEyOklzc3VlQ29tbWVudDQ4MDI0ODcyNg==",
- "updated_at": "2019-04-05T11:55:43Z",
- "url": "https://api.github.com/repos/ursa-labs/ursabot/issues/comments/480248726",
- "user": {
- "avatar_url": "https://avatars1.githubusercontent.com/u/961747?v=4",
- "events_url": "https://api.github.com/users/kszucs/events{/privacy}",
- "followers_url": "https://api.github.com/users/kszucs/followers",
- "following_url": "https://api.github.com/users/kszucs/following{/other_user}",
- "gists_url": "https://api.github.com/users/kszucs/gists{/gist_id}",
- "gravatar_id": "",
- "html_url": "https://github.com/kszucs",
- "id": 961747,
- "login": "kszucs",
- "node_id": "MDQ6VXNlcjk2MTc0Nw==",
- "organizations_url": "https://api.github.com/users/kszucs/orgs",
- "received_events_url": "https://api.github.com/users/kszucs/received_events",
- "repos_url": "https://api.github.com/users/kszucs/repos",
- "site_admin": false,
- "starred_url": "https://api.github.com/users/kszucs/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/kszucs/subscriptions",
- "type": "User",
- "url": "https://api.github.com/users/kszucs"
- }
- },
- "issue": {
- "assignee": null,
- "assignees": [],
- "author_association": "MEMBER",
- "body": "",
- "closed_at": null,
- "comments": 3,
- "comments_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/26/comments",
- "created_at": "2019-04-05T11:22:15Z",
- "events_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/26/events",
- "html_url": "https://github.com/ursa-labs/ursabot/pull/26",
- "id": 429706959,
- "labels": [],
- "labels_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/26/labels{/name}",
- "locked": false,
- "milestone": null,
- "node_id": "MDExOlB1bGxSZXF1ZXN0MjY3Nzg1NTUy",
- "number": 26,
- "pull_request": {
- "diff_url": "https://github.com/ursa-labs/ursabot/pull/26.diff",
- "html_url": "https://github.com/ursa-labs/ursabot/pull/26",
- "patch_url": "https://github.com/ursa-labs/ursabot/pull/26.patch",
- "url": "https://api.github.com/repos/ursa-labs/ursabot/pulls/26"
- },
- "repository_url": "https://api.github.com/repos/ursa-labs/ursabot",
- "state": "open",
- "title": "Unittests for GithubHook",
- "updated_at": "2019-04-05T11:55:43Z",
- "url": "https://api.github.com/repos/ursa-labs/ursabot/issues/26",
- "user": {
- "avatar_url": "https://avatars1.githubusercontent.com/u/961747?v=4",
- "events_url": "https://api.github.com/users/kszucs/events{/privacy}",
- "followers_url": "https://api.github.com/users/kszucs/followers",
- "following_url": "https://api.github.com/users/kszucs/following{/other_user}",
- "gists_url": "https://api.github.com/users/kszucs/gists{/gist_id}",
- "gravatar_id": "",
- "html_url": "https://github.com/kszucs",
- "id": 961747,
- "login": "kszucs",
- "node_id": "MDQ6VXNlcjk2MTc0Nw==",
- "organizations_url": "https://api.github.com/users/kszucs/orgs",
- "received_events_url": "https://api.github.com/users/kszucs/received_events",
- "repos_url": "https://api.github.com/users/kszucs/repos",
- "site_admin": false,
- "starred_url": "https://api.github.com/users/kszucs/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/kszucs/subscriptions",
- "type": "User",
- "url": "https://api.github.com/users/kszucs"
- }
- },
- "organization": {
- "avatar_url": "https://avatars2.githubusercontent.com/u/46514972?v=4",
- "description": "Innovation lab for open source data science tools, powered by Apache Arrow",
- "events_url": "https://api.github.com/orgs/ursa-labs/events",
- "hooks_url": "https://api.github.com/orgs/ursa-labs/hooks",
- "id": 46514972,
- "issues_url": "https://api.github.com/orgs/ursa-labs/issues",
- "login": "ursa-labs",
- "members_url": "https://api.github.com/orgs/ursa-labs/members{/member}",
- "node_id": "MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy",
- "public_members_url": "https://api.github.com/orgs/ursa-labs/public_members{/member}",
- "repos_url": "https://api.github.com/orgs/ursa-labs/repos",
- "url": "https://api.github.com/orgs/ursa-labs"
- },
- "repository": {
- "archive_url": "https://api.github.com/repos/ursa-labs/ursabot/{archive_format}{/ref}",
- "archived": false,
- "assignees_url": "https://api.github.com/repos/ursa-labs/ursabot/assignees{/user}",
- "blobs_url": "https://api.github.com/repos/ursa-labs/ursabot/git/blobs{/sha}",
- "branches_url": "https://api.github.com/repos/ursa-labs/ursabot/branches{/branch}",
- "clone_url": "https://github.com/ursa-labs/ursabot.git",
- "collaborators_url": "https://api.github.com/repos/ursa-labs/ursabot/collaborators{/collaborator}",
- "comments_url": "https://api.github.com/repos/ursa-labs/ursabot/comments{/number}",
- "commits_url": "https://api.github.com/repos/ursa-labs/ursabot/commits{/sha}",
- "compare_url": "https://api.github.com/repos/ursa-labs/ursabot/compare/{base}...{head}",
- "contents_url": "https://api.github.com/repos/ursa-labs/ursabot/contents/{+path}",
- "contributors_url": "https://api.github.com/repos/ursa-labs/ursabot/contributors",
- "created_at": "2019-02-04T15:40:31Z",
- "default_branch": "master",
- "deployments_url": "https://api.github.com/repos/ursa-labs/ursabot/deployments",
- "description": null,
- "disabled": false,
- "downloads_url": "https://api.github.com/repos/ursa-labs/ursabot/downloads",
- "events_url": "https://api.github.com/repos/ursa-labs/ursabot/events",
- "fork": false,
- "forks": 0,
- "forks_count": 0,
- "forks_url": "https://api.github.com/repos/ursa-labs/ursabot/forks",
- "full_name": "ursa-labs/ursabot",
- "git_commits_url": "https://api.github.com/repos/ursa-labs/ursabot/git/commits{/sha}",
- "git_refs_url": "https://api.github.com/repos/ursa-labs/ursabot/git/refs{/sha}",
- "git_tags_url": "https://api.github.com/repos/ursa-labs/ursabot/git/tags{/sha}",
- "git_url": "git://github.com/ursa-labs/ursabot.git",
- "has_downloads": true,
- "has_issues": true,
- "has_pages": false,
- "has_projects": true,
- "has_wiki": true,
- "homepage": null,
- "hooks_url": "https://api.github.com/repos/ursa-labs/ursabot/hooks",
- "html_url": "https://github.com/ursa-labs/ursabot",
- "id": 169101701,
- "issue_comment_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/comments{/number}",
- "issue_events_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/events{/number}",
- "issues_url": "https://api.github.com/repos/ursa-labs/ursabot/issues{/number}",
- "keys_url": "https://api.github.com/repos/ursa-labs/ursabot/keys{/key_id}",
- "labels_url": "https://api.github.com/repos/ursa-labs/ursabot/labels{/name}",
- "language": "Jupyter Notebook",
- "languages_url": "https://api.github.com/repos/ursa-labs/ursabot/languages",
- "license": null,
- "merges_url": "https://api.github.com/repos/ursa-labs/ursabot/merges",
- "milestones_url": "https://api.github.com/repos/ursa-labs/ursabot/milestones{/number}",
- "mirror_url": null,
- "name": "ursabot",
- "node_id": "MDEwOlJlcG9zaXRvcnkxNjkxMDE3MDE=",
- "notifications_url": "https://api.github.com/repos/ursa-labs/ursabot/notifications{?since,all,participating}",
- "open_issues": 19,
- "open_issues_count": 19,
- "owner": {
- "avatar_url": "https://avatars2.githubusercontent.com/u/46514972?v=4",
- "events_url": "https://api.github.com/users/ursa-labs/events{/privacy}",
- "followers_url": "https://api.github.com/users/ursa-labs/followers",
- "following_url": "https://api.github.com/users/ursa-labs/following{/other_user}",
- "gists_url": "https://api.github.com/users/ursa-labs/gists{/gist_id}",
- "gravatar_id": "",
- "html_url": "https://github.com/ursa-labs",
- "id": 46514972,
- "login": "ursa-labs",
- "node_id": "MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy",
- "organizations_url": "https://api.github.com/users/ursa-labs/orgs",
- "received_events_url": "https://api.github.com/users/ursa-labs/received_events",
- "repos_url": "https://api.github.com/users/ursa-labs/repos",
- "site_admin": false,
- "starred_url": "https://api.github.com/users/ursa-labs/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/ursa-labs/subscriptions",
- "type": "Organization",
- "url": "https://api.github.com/users/ursa-labs"
- },
- "private": false,
- "pulls_url": "https://api.github.com/repos/ursa-labs/ursabot/pulls{/number}",
- "pushed_at": "2019-04-05T11:22:16Z",
- "releases_url": "https://api.github.com/repos/ursa-labs/ursabot/releases{/id}",
- "size": 892,
- "ssh_url": "git@github.com:ursa-labs/ursabot.git",
- "stargazers_count": 1,
- "stargazers_url": "https://api.github.com/repos/ursa-labs/ursabot/stargazers",
- "statuses_url": "https://api.github.com/repos/ursa-labs/ursabot/statuses/{sha}",
- "subscribers_url": "https://api.github.com/repos/ursa-labs/ursabot/subscribers",
- "subscription_url": "https://api.github.com/repos/ursa-labs/ursabot/subscription",
- "svn_url": "https://github.com/ursa-labs/ursabot",
- "tags_url": "https://api.github.com/repos/ursa-labs/ursabot/tags",
- "teams_url": "https://api.github.com/repos/ursa-labs/ursabot/teams",
- "trees_url": "https://api.github.com/repos/ursa-labs/ursabot/git/trees{/sha}",
- "updated_at": "2019-04-04T17:49:10Z",
- "url": "https://api.github.com/repos/ursa-labs/ursabot",
- "watchers": 1,
- "watchers_count": 1
- },
- "sender": {
- "avatar_url": "https://avatars1.githubusercontent.com/u/961747?v=4",
- "events_url": "https://api.github.com/users/kszucs/events{/privacy}",
- "followers_url": "https://api.github.com/users/kszucs/followers",
- "following_url": "https://api.github.com/users/kszucs/following{/other_user}",
- "gists_url": "https://api.github.com/users/kszucs/gists{/gist_id}",
- "gravatar_id": "",
- "html_url": "https://github.com/kszucs",
- "id": 961747,
- "login": "kszucs",
- "node_id": "MDQ6VXNlcjk2MTc0Nw==",
- "organizations_url": "https://api.github.com/users/kszucs/orgs",
- "received_events_url": "https://api.github.com/users/kszucs/received_events",
- "repos_url": "https://api.github.com/users/kszucs/repos",
- "site_admin": false,
- "starred_url": "https://api.github.com/users/kszucs/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/kszucs/subscriptions",
- "type": "User",
- "url": "https://api.github.com/users/kszucs"
- }
-}
\ No newline at end of file
diff --git a/dev/archery/archery/tests/fixtures/event-issue-comment-by-non-authorized-user.json b/dev/archery/archery/tests/fixtures/event-issue-comment-by-non-authorized-user.json
deleted file mode 100644
index 5a8f346..0000000
--- a/dev/archery/archery/tests/fixtures/event-issue-comment-by-non-authorized-user.json
+++ /dev/null
@@ -1,212 +0,0 @@
-{
- "action": "created",
- "comment": {
- "author_association": "NONE",
- "body": "Unknown command \"\"",
- "created_at": "2019-04-05T11:35:47Z",
- "html_url": "https://github.com/ursa-labs/ursabot/pull/26#issuecomment-480243815",
- "id": 480243815,
- "issue_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/26",
- "node_id": "MDEyOklzc3VlQ29tbWVudDQ4MDI0MzgxNQ==",
- "updated_at": "2019-04-05T11:35:47Z",
- "url": "https://api.github.com/repos/ursa-labs/ursabot/issues/comments/480243815",
- "user": {
- "avatar_url": "https://avatars2.githubusercontent.com/u/49275095?v=4",
- "events_url": "https://api.github.com/users/ursabot/events{/privacy}",
- "followers_url": "https://api.github.com/users/ursabot/followers",
- "following_url": "https://api.github.com/users/ursabot/following{/other_user}",
- "gists_url": "https://api.github.com/users/ursabot/gists{/gist_id}",
- "gravatar_id": "",
- "html_url": "https://github.com/ursabot",
- "id": 49275095,
- "login": "someone",
- "node_id": "MDQ6VXNlcjQ5Mjc1MDk1",
- "organizations_url": "https://api.github.com/users/ursabot/orgs",
- "received_events_url": "https://api.github.com/users/ursabot/received_events",
- "repos_url": "https://api.github.com/users/ursabot/repos",
- "site_admin": false,
- "starred_url": "https://api.github.com/users/ursabot/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/ursabot/subscriptions",
- "type": "User",
- "url": "https://api.github.com/users/ursabot"
- }
- },
- "issue": {
- "assignee": null,
- "assignees": [],
- "author_association": "NONE",
- "body": "",
- "closed_at": null,
- "comments": 2,
- "comments_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/26/comments",
- "created_at": "2019-04-05T11:22:15Z",
- "events_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/26/events",
- "html_url": "https://github.com/ursa-labs/ursabot/pull/26",
- "id": 429706959,
- "labels": [],
- "labels_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/26/labels{/name}",
- "locked": false,
- "milestone": null,
- "node_id": "MDExOlB1bGxSZXF1ZXN0MjY3Nzg1NTUy",
- "number": 26,
- "pull_request": {
- "diff_url": "https://github.com/ursa-labs/ursabot/pull/26.diff",
- "html_url": "https://github.com/ursa-labs/ursabot/pull/26",
- "patch_url": "https://github.com/ursa-labs/ursabot/pull/26.patch",
- "url": "https://api.github.com/repos/ursa-labs/ursabot/pulls/26"
- },
- "repository_url": "https://api.github.com/repos/ursa-labs/ursabot",
- "state": "open",
- "title": "Unittests for GithubHook",
- "updated_at": "2019-04-05T11:35:47Z",
- "url": "https://api.github.com/repos/ursa-labs/ursabot/issues/26",
- "user": {
- "avatar_url": "https://avatars1.githubusercontent.com/u/961747?v=4",
- "events_url": "https://api.github.com/users/kszucs/events{/privacy}",
- "followers_url": "https://api.github.com/users/kszucs/followers",
- "following_url": "https://api.github.com/users/kszucs/following{/other_user}",
- "gists_url": "https://api.github.com/users/kszucs/gists{/gist_id}",
- "gravatar_id": "",
- "html_url": "https://github.com/kszucs",
- "id": 961747,
- "login": "kszucs",
- "node_id": "MDQ6VXNlcjk2MTc0Nw==",
- "organizations_url": "https://api.github.com/users/kszucs/orgs",
- "received_events_url": "https://api.github.com/users/kszucs/received_events",
- "repos_url": "https://api.github.com/users/kszucs/repos",
- "site_admin": false,
- "starred_url": "https://api.github.com/users/kszucs/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/kszucs/subscriptions",
- "type": "User",
- "url": "https://api.github.com/users/kszucs"
- }
- },
- "organization": {
- "avatar_url": "https://avatars2.githubusercontent.com/u/46514972?v=4",
- "description": "Innovation lab for open source data science tools, powered by Apache Arrow",
- "events_url": "https://api.github.com/orgs/ursa-labs/events",
- "hooks_url": "https://api.github.com/orgs/ursa-labs/hooks",
- "id": 46514972,
- "issues_url": "https://api.github.com/orgs/ursa-labs/issues",
- "login": "ursa-labs",
- "members_url": "https://api.github.com/orgs/ursa-labs/members{/member}",
- "node_id": "MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy",
- "public_members_url": "https://api.github.com/orgs/ursa-labs/public_members{/member}",
- "repos_url": "https://api.github.com/orgs/ursa-labs/repos",
- "url": "https://api.github.com/orgs/ursa-labs"
- },
- "repository": {
- "archive_url": "https://api.github.com/repos/ursa-labs/ursabot/{archive_format}{/ref}",
- "archived": false,
- "assignees_url": "https://api.github.com/repos/ursa-labs/ursabot/assignees{/user}",
- "blobs_url": "https://api.github.com/repos/ursa-labs/ursabot/git/blobs{/sha}",
- "branches_url": "https://api.github.com/repos/ursa-labs/ursabot/branches{/branch}",
- "clone_url": "https://github.com/ursa-labs/ursabot.git",
- "collaborators_url": "https://api.github.com/repos/ursa-labs/ursabot/collaborators{/collaborator}",
- "comments_url": "https://api.github.com/repos/ursa-labs/ursabot/comments{/number}",
- "commits_url": "https://api.github.com/repos/ursa-labs/ursabot/commits{/sha}",
- "compare_url": "https://api.github.com/repos/ursa-labs/ursabot/compare/{base}...{head}",
- "contents_url": "https://api.github.com/repos/ursa-labs/ursabot/contents/{+path}",
- "contributors_url": "https://api.github.com/repos/ursa-labs/ursabot/contributors",
- "created_at": "2019-02-04T15:40:31Z",
- "default_branch": "master",
- "deployments_url": "https://api.github.com/repos/ursa-labs/ursabot/deployments",
- "description": null,
- "disabled": false,
- "downloads_url": "https://api.github.com/repos/ursa-labs/ursabot/downloads",
- "events_url": "https://api.github.com/repos/ursa-labs/ursabot/events",
- "fork": false,
- "forks": 0,
- "forks_count": 0,
- "forks_url": "https://api.github.com/repos/ursa-labs/ursabot/forks",
- "full_name": "ursa-labs/ursabot",
- "git_commits_url": "https://api.github.com/repos/ursa-labs/ursabot/git/commits{/sha}",
- "git_refs_url": "https://api.github.com/repos/ursa-labs/ursabot/git/refs{/sha}",
- "git_tags_url": "https://api.github.com/repos/ursa-labs/ursabot/git/tags{/sha}",
- "git_url": "git://github.com/ursa-labs/ursabot.git",
- "has_downloads": true,
- "has_issues": true,
- "has_pages": false,
- "has_projects": true,
- "has_wiki": true,
- "homepage": null,
- "hooks_url": "https://api.github.com/repos/ursa-labs/ursabot/hooks",
- "html_url": "https://github.com/ursa-labs/ursabot",
- "id": 169101701,
- "issue_comment_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/comments{/number}",
- "issue_events_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/events{/number}",
- "issues_url": "https://api.github.com/repos/ursa-labs/ursabot/issues{/number}",
- "keys_url": "https://api.github.com/repos/ursa-labs/ursabot/keys{/key_id}",
- "labels_url": "https://api.github.com/repos/ursa-labs/ursabot/labels{/name}",
- "language": "Jupyter Notebook",
- "languages_url": "https://api.github.com/repos/ursa-labs/ursabot/languages",
- "license": null,
- "merges_url": "https://api.github.com/repos/ursa-labs/ursabot/merges",
- "milestones_url": "https://api.github.com/repos/ursa-labs/ursabot/milestones{/number}",
- "mirror_url": null,
- "name": "someone",
- "node_id": "MDEwOlJlcG9zaXRvcnkxNjkxMDE3MDE=",
- "notifications_url": "https://api.github.com/repos/ursa-labs/ursabot/notifications{?since,all,participating}",
- "open_issues": 19,
- "open_issues_count": 19,
- "owner": {
- "avatar_url": "https://avatars2.githubusercontent.com/u/46514972?v=4",
- "events_url": "https://api.github.com/users/ursa-labs/events{/privacy}",
- "followers_url": "https://api.github.com/users/ursa-labs/followers",
- "following_url": "https://api.github.com/users/ursa-labs/following{/other_user}",
- "gists_url": "https://api.github.com/users/ursa-labs/gists{/gist_id}",
- "gravatar_id": "",
- "html_url": "https://github.com/ursa-labs",
- "id": 46514972,
- "login": "ursa-labs",
- "node_id": "MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy",
- "organizations_url": "https://api.github.com/users/ursa-labs/orgs",
- "received_events_url": "https://api.github.com/users/ursa-labs/received_events",
- "repos_url": "https://api.github.com/users/ursa-labs/repos",
- "site_admin": false,
- "starred_url": "https://api.github.com/users/ursa-labs/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/ursa-labs/subscriptions",
- "type": "Organization",
- "url": "https://api.github.com/users/ursa-labs"
- },
- "private": false,
- "pulls_url": "https://api.github.com/repos/ursa-labs/ursabot/pulls{/number}",
- "pushed_at": "2019-04-05T11:22:16Z",
- "releases_url": "https://api.github.com/repos/ursa-labs/ursabot/releases{/id}",
- "size": 892,
- "ssh_url": "git@github.com:ursa-labs/ursabot.git",
- "stargazers_count": 1,
- "stargazers_url": "https://api.github.com/repos/ursa-labs/ursabot/stargazers",
- "statuses_url": "https://api.github.com/repos/ursa-labs/ursabot/statuses/{sha}",
- "subscribers_url": "https://api.github.com/repos/ursa-labs/ursabot/subscribers",
- "subscription_url": "https://api.github.com/repos/ursa-labs/ursabot/subscription",
- "svn_url": "https://github.com/ursa-labs/ursabot",
- "tags_url": "https://api.github.com/repos/ursa-labs/ursabot/tags",
- "teams_url": "https://api.github.com/repos/ursa-labs/ursabot/teams",
- "trees_url": "https://api.github.com/repos/ursa-labs/ursabot/git/trees{/sha}",
- "updated_at": "2019-04-04T17:49:10Z",
- "url": "https://api.github.com/repos/ursa-labs/ursabot",
- "watchers": 1,
- "watchers_count": 1
- },
- "sender": {
- "avatar_url": "https://avatars2.githubusercontent.com/u/49275095?v=4",
- "events_url": "https://api.github.com/users/ursabot/events{/privacy}",
- "followers_url": "https://api.github.com/users/ursabot/followers",
- "following_url": "https://api.github.com/users/ursabot/following{/other_user}",
- "gists_url": "https://api.github.com/users/ursabot/gists{/gist_id}",
- "gravatar_id": "",
- "html_url": "https://github.com/ursabot",
- "id": 49275095,
- "login": "someone",
- "node_id": "MDQ6VXNlcjQ5Mjc1MDk1",
- "organizations_url": "https://api.github.com/users/ursabot/orgs",
- "received_events_url": "https://api.github.com/users/ursabot/received_events",
- "repos_url": "https://api.github.com/users/ursabot/repos",
- "site_admin": false,
- "starred_url": "https://api.github.com/users/ursabot/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/ursabot/subscriptions",
- "type": "User",
- "url": "https://api.github.com/users/ursabot"
- }
-}
\ No newline at end of file
diff --git a/dev/archery/archery/tests/fixtures/event-issue-comment-by-ursabot.json b/dev/archery/archery/tests/fixtures/event-issue-comment-by-ursabot.json
deleted file mode 100644
index bfb7210..0000000
--- a/dev/archery/archery/tests/fixtures/event-issue-comment-by-ursabot.json
+++ /dev/null
@@ -1,212 +0,0 @@
-{
- "action": "created",
- "comment": {
- "author_association": "NONE",
- "body": "Unknown command \"\"",
- "created_at": "2019-04-05T11:35:47Z",
- "html_url": "https://github.com/ursa-labs/ursabot/pull/26#issuecomment-480243815",
- "id": 480243815,
- "issue_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/26",
- "node_id": "MDEyOklzc3VlQ29tbWVudDQ4MDI0MzgxNQ==",
- "updated_at": "2019-04-05T11:35:47Z",
- "url": "https://api.github.com/repos/ursa-labs/ursabot/issues/comments/480243815",
- "user": {
- "avatar_url": "https://avatars2.githubusercontent.com/u/49275095?v=4",
- "events_url": "https://api.github.com/users/ursabot/events{/privacy}",
- "followers_url": "https://api.github.com/users/ursabot/followers",
- "following_url": "https://api.github.com/users/ursabot/following{/other_user}",
- "gists_url": "https://api.github.com/users/ursabot/gists{/gist_id}",
- "gravatar_id": "",
- "html_url": "https://github.com/ursabot",
- "id": 49275095,
- "login": "ursabot",
- "node_id": "MDQ6VXNlcjQ5Mjc1MDk1",
- "organizations_url": "https://api.github.com/users/ursabot/orgs",
- "received_events_url": "https://api.github.com/users/ursabot/received_events",
- "repos_url": "https://api.github.com/users/ursabot/repos",
- "site_admin": false,
- "starred_url": "https://api.github.com/users/ursabot/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/ursabot/subscriptions",
- "type": "User",
- "url": "https://api.github.com/users/ursabot"
- }
- },
- "issue": {
- "assignee": null,
- "assignees": [],
- "author_association": "MEMBER",
- "body": "",
- "closed_at": null,
- "comments": 2,
- "comments_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/26/comments",
- "created_at": "2019-04-05T11:22:15Z",
- "events_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/26/events",
- "html_url": "https://github.com/ursa-labs/ursabot/pull/26",
- "id": 429706959,
- "labels": [],
- "labels_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/26/labels{/name}",
- "locked": false,
- "milestone": null,
- "node_id": "MDExOlB1bGxSZXF1ZXN0MjY3Nzg1NTUy",
- "number": 26,
- "pull_request": {
- "diff_url": "https://github.com/ursa-labs/ursabot/pull/26.diff",
- "html_url": "https://github.com/ursa-labs/ursabot/pull/26",
- "patch_url": "https://github.com/ursa-labs/ursabot/pull/26.patch",
- "url": "https://api.github.com/repos/ursa-labs/ursabot/pulls/26"
- },
- "repository_url": "https://api.github.com/repos/ursa-labs/ursabot",
- "state": "open",
- "title": "Unittests for GithubHook",
- "updated_at": "2019-04-05T11:35:47Z",
- "url": "https://api.github.com/repos/ursa-labs/ursabot/issues/26",
- "user": {
- "avatar_url": "https://avatars1.githubusercontent.com/u/961747?v=4",
- "events_url": "https://api.github.com/users/kszucs/events{/privacy}",
- "followers_url": "https://api.github.com/users/kszucs/followers",
- "following_url": "https://api.github.com/users/kszucs/following{/other_user}",
- "gists_url": "https://api.github.com/users/kszucs/gists{/gist_id}",
- "gravatar_id": "",
- "html_url": "https://github.com/kszucs",
- "id": 961747,
- "login": "kszucs",
- "node_id": "MDQ6VXNlcjk2MTc0Nw==",
- "organizations_url": "https://api.github.com/users/kszucs/orgs",
- "received_events_url": "https://api.github.com/users/kszucs/received_events",
- "repos_url": "https://api.github.com/users/kszucs/repos",
- "site_admin": false,
- "starred_url": "https://api.github.com/users/kszucs/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/kszucs/subscriptions",
- "type": "User",
- "url": "https://api.github.com/users/kszucs"
- }
- },
- "organization": {
- "avatar_url": "https://avatars2.githubusercontent.com/u/46514972?v=4",
- "description": "Innovation lab for open source data science tools, powered by Apache Arrow",
- "events_url": "https://api.github.com/orgs/ursa-labs/events",
- "hooks_url": "https://api.github.com/orgs/ursa-labs/hooks",
- "id": 46514972,
- "issues_url": "https://api.github.com/orgs/ursa-labs/issues",
- "login": "ursa-labs",
- "members_url": "https://api.github.com/orgs/ursa-labs/members{/member}",
- "node_id": "MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy",
- "public_members_url": "https://api.github.com/orgs/ursa-labs/public_members{/member}",
- "repos_url": "https://api.github.com/orgs/ursa-labs/repos",
- "url": "https://api.github.com/orgs/ursa-labs"
- },
- "repository": {
- "archive_url": "https://api.github.com/repos/ursa-labs/ursabot/{archive_format}{/ref}",
- "archived": false,
- "assignees_url": "https://api.github.com/repos/ursa-labs/ursabot/assignees{/user}",
- "blobs_url": "https://api.github.com/repos/ursa-labs/ursabot/git/blobs{/sha}",
- "branches_url": "https://api.github.com/repos/ursa-labs/ursabot/branches{/branch}",
- "clone_url": "https://github.com/ursa-labs/ursabot.git",
- "collaborators_url": "https://api.github.com/repos/ursa-labs/ursabot/collaborators{/collaborator}",
- "comments_url": "https://api.github.com/repos/ursa-labs/ursabot/comments{/number}",
- "commits_url": "https://api.github.com/repos/ursa-labs/ursabot/commits{/sha}",
- "compare_url": "https://api.github.com/repos/ursa-labs/ursabot/compare/{base}...{head}",
- "contents_url": "https://api.github.com/repos/ursa-labs/ursabot/contents/{+path}",
- "contributors_url": "https://api.github.com/repos/ursa-labs/ursabot/contributors",
- "created_at": "2019-02-04T15:40:31Z",
- "default_branch": "master",
- "deployments_url": "https://api.github.com/repos/ursa-labs/ursabot/deployments",
- "description": null,
- "disabled": false,
- "downloads_url": "https://api.github.com/repos/ursa-labs/ursabot/downloads",
- "events_url": "https://api.github.com/repos/ursa-labs/ursabot/events",
- "fork": false,
- "forks": 0,
- "forks_count": 0,
- "forks_url": "https://api.github.com/repos/ursa-labs/ursabot/forks",
- "full_name": "ursa-labs/ursabot",
- "git_commits_url": "https://api.github.com/repos/ursa-labs/ursabot/git/commits{/sha}",
- "git_refs_url": "https://api.github.com/repos/ursa-labs/ursabot/git/refs{/sha}",
- "git_tags_url": "https://api.github.com/repos/ursa-labs/ursabot/git/tags{/sha}",
- "git_url": "git://github.com/ursa-labs/ursabot.git",
- "has_downloads": true,
- "has_issues": true,
- "has_pages": false,
- "has_projects": true,
- "has_wiki": true,
- "homepage": null,
- "hooks_url": "https://api.github.com/repos/ursa-labs/ursabot/hooks",
- "html_url": "https://github.com/ursa-labs/ursabot",
- "id": 169101701,
- "issue_comment_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/comments{/number}",
- "issue_events_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/events{/number}",
- "issues_url": "https://api.github.com/repos/ursa-labs/ursabot/issues{/number}",
- "keys_url": "https://api.github.com/repos/ursa-labs/ursabot/keys{/key_id}",
- "labels_url": "https://api.github.com/repos/ursa-labs/ursabot/labels{/name}",
- "language": "Jupyter Notebook",
- "languages_url": "https://api.github.com/repos/ursa-labs/ursabot/languages",
- "license": null,
- "merges_url": "https://api.github.com/repos/ursa-labs/ursabot/merges",
- "milestones_url": "https://api.github.com/repos/ursa-labs/ursabot/milestones{/number}",
- "mirror_url": null,
- "name": "ursabot",
- "node_id": "MDEwOlJlcG9zaXRvcnkxNjkxMDE3MDE=",
- "notifications_url": "https://api.github.com/repos/ursa-labs/ursabot/notifications{?since,all,participating}",
- "open_issues": 19,
- "open_issues_count": 19,
- "owner": {
- "avatar_url": "https://avatars2.githubusercontent.com/u/46514972?v=4",
- "events_url": "https://api.github.com/users/ursa-labs/events{/privacy}",
- "followers_url": "https://api.github.com/users/ursa-labs/followers",
- "following_url": "https://api.github.com/users/ursa-labs/following{/other_user}",
- "gists_url": "https://api.github.com/users/ursa-labs/gists{/gist_id}",
- "gravatar_id": "",
- "html_url": "https://github.com/ursa-labs",
- "id": 46514972,
- "login": "ursa-labs",
- "node_id": "MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy",
- "organizations_url": "https://api.github.com/users/ursa-labs/orgs",
- "received_events_url": "https://api.github.com/users/ursa-labs/received_events",
- "repos_url": "https://api.github.com/users/ursa-labs/repos",
- "site_admin": false,
- "starred_url": "https://api.github.com/users/ursa-labs/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/ursa-labs/subscriptions",
- "type": "Organization",
- "url": "https://api.github.com/users/ursa-labs"
- },
- "private": false,
- "pulls_url": "https://api.github.com/repos/ursa-labs/ursabot/pulls{/number}",
- "pushed_at": "2019-04-05T11:22:16Z",
- "releases_url": "https://api.github.com/repos/ursa-labs/ursabot/releases{/id}",
- "size": 892,
- "ssh_url": "git@github.com:ursa-labs/ursabot.git",
- "stargazers_count": 1,
- "stargazers_url": "https://api.github.com/repos/ursa-labs/ursabot/stargazers",
- "statuses_url": "https://api.github.com/repos/ursa-labs/ursabot/statuses/{sha}",
- "subscribers_url": "https://api.github.com/repos/ursa-labs/ursabot/subscribers",
- "subscription_url": "https://api.github.com/repos/ursa-labs/ursabot/subscription",
- "svn_url": "https://github.com/ursa-labs/ursabot",
- "tags_url": "https://api.github.com/repos/ursa-labs/ursabot/tags",
- "teams_url": "https://api.github.com/repos/ursa-labs/ursabot/teams",
- "trees_url": "https://api.github.com/repos/ursa-labs/ursabot/git/trees{/sha}",
- "updated_at": "2019-04-04T17:49:10Z",
- "url": "https://api.github.com/repos/ursa-labs/ursabot",
- "watchers": 1,
- "watchers_count": 1
- },
- "sender": {
- "avatar_url": "https://avatars2.githubusercontent.com/u/49275095?v=4",
- "events_url": "https://api.github.com/users/ursabot/events{/privacy}",
- "followers_url": "https://api.github.com/users/ursabot/followers",
- "following_url": "https://api.github.com/users/ursabot/following{/other_user}",
- "gists_url": "https://api.github.com/users/ursabot/gists{/gist_id}",
- "gravatar_id": "",
- "html_url": "https://github.com/ursabot",
- "id": 49275095,
- "login": "ursabot",
- "node_id": "MDQ6VXNlcjQ5Mjc1MDk1",
- "organizations_url": "https://api.github.com/users/ursabot/orgs",
- "received_events_url": "https://api.github.com/users/ursabot/received_events",
- "repos_url": "https://api.github.com/users/ursabot/repos",
- "site_admin": false,
- "starred_url": "https://api.github.com/users/ursabot/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/ursabot/subscriptions",
- "type": "User",
- "url": "https://api.github.com/users/ursabot"
- }
-}
\ No newline at end of file
diff --git a/dev/archery/archery/tests/fixtures/event-issue-comment-not-mentioning-ursabot.json b/dev/archery/archery/tests/fixtures/event-issue-comment-not-mentioning-ursabot.json
deleted file mode 100644
index a3d4500..0000000
--- a/dev/archery/archery/tests/fixtures/event-issue-comment-not-mentioning-ursabot.json
+++ /dev/null
@@ -1,212 +0,0 @@
-{
- "action": "created",
- "comment": {
- "author_association": "MEMBER",
- "body": "bear is no game",
- "created_at": "2019-04-05T11:26:56Z",
- "html_url": "https://github.com/ursa-labs/ursabot/pull/26#issuecomment-480241727",
- "id": 480241727,
- "issue_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/26",
- "node_id": "MDEyOklzc3VlQ29tbWVudDQ4MDI0MTcyNw==",
- "updated_at": "2019-04-05T11:26:56Z",
- "url": "https://api.github.com/repos/ursa-labs/ursabot/issues/comments/480241727",
- "user": {
- "avatar_url": "https://avatars1.githubusercontent.com/u/961747?v=4",
- "events_url": "https://api.github.com/users/kszucs/events{/privacy}",
- "followers_url": "https://api.github.com/users/kszucs/followers",
- "following_url": "https://api.github.com/users/kszucs/following{/other_user}",
- "gists_url": "https://api.github.com/users/kszucs/gists{/gist_id}",
- "gravatar_id": "",
- "html_url": "https://github.com/kszucs",
- "id": 961747,
- "login": "kszucs",
- "node_id": "MDQ6VXNlcjk2MTc0Nw==",
- "organizations_url": "https://api.github.com/users/kszucs/orgs",
- "received_events_url": "https://api.github.com/users/kszucs/received_events",
- "repos_url": "https://api.github.com/users/kszucs/repos",
- "site_admin": false,
- "starred_url": "https://api.github.com/users/kszucs/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/kszucs/subscriptions",
- "type": "User",
- "url": "https://api.github.com/users/kszucs"
- }
- },
- "issue": {
- "assignee": null,
- "assignees": [],
- "author_association": "MEMBER",
- "body": "",
- "closed_at": null,
- "comments": 0,
- "comments_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/26/comments",
- "created_at": "2019-04-05T11:22:15Z",
- "events_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/26/events",
- "html_url": "https://github.com/ursa-labs/ursabot/pull/26",
- "id": 429706959,
- "labels": [],
- "labels_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/26/labels{/name}",
- "locked": false,
- "milestone": null,
- "node_id": "MDExOlB1bGxSZXF1ZXN0MjY3Nzg1NTUy",
- "number": 26,
- "pull_request": {
- "diff_url": "https://github.com/ursa-labs/ursabot/pull/26.diff",
- "html_url": "https://github.com/ursa-labs/ursabot/pull/26",
- "patch_url": "https://github.com/ursa-labs/ursabot/pull/26.patch",
- "url": "https://api.github.com/repos/ursa-labs/ursabot/pulls/26"
- },
- "repository_url": "https://api.github.com/repos/ursa-labs/ursabot",
- "state": "open",
- "title": "Unittests for GithubHook",
- "updated_at": "2019-04-05T11:26:56Z",
- "url": "https://api.github.com/repos/ursa-labs/ursabot/issues/26",
- "user": {
- "avatar_url": "https://avatars1.githubusercontent.com/u/961747?v=4",
- "events_url": "https://api.github.com/users/kszucs/events{/privacy}",
- "followers_url": "https://api.github.com/users/kszucs/followers",
- "following_url": "https://api.github.com/users/kszucs/following{/other_user}",
- "gists_url": "https://api.github.com/users/kszucs/gists{/gist_id}",
- "gravatar_id": "",
- "html_url": "https://github.com/kszucs",
- "id": 961747,
- "login": "kszucs",
- "node_id": "MDQ6VXNlcjk2MTc0Nw==",
- "organizations_url": "https://api.github.com/users/kszucs/orgs",
- "received_events_url": "https://api.github.com/users/kszucs/received_events",
- "repos_url": "https://api.github.com/users/kszucs/repos",
- "site_admin": false,
- "starred_url": "https://api.github.com/users/kszucs/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/kszucs/subscriptions",
- "type": "User",
- "url": "https://api.github.com/users/kszucs"
- }
- },
- "organization": {
- "avatar_url": "https://avatars2.githubusercontent.com/u/46514972?v=4",
- "description": "Innovation lab for open source data science tools, powered by Apache Arrow",
- "events_url": "https://api.github.com/orgs/ursa-labs/events",
- "hooks_url": "https://api.github.com/orgs/ursa-labs/hooks",
- "id": 46514972,
- "issues_url": "https://api.github.com/orgs/ursa-labs/issues",
- "login": "ursa-labs",
- "members_url": "https://api.github.com/orgs/ursa-labs/members{/member}",
- "node_id": "MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy",
- "public_members_url": "https://api.github.com/orgs/ursa-labs/public_members{/member}",
- "repos_url": "https://api.github.com/orgs/ursa-labs/repos",
- "url": "https://api.github.com/orgs/ursa-labs"
- },
- "repository": {
- "archive_url": "https://api.github.com/repos/ursa-labs/ursabot/{archive_format}{/ref}",
- "archived": false,
- "assignees_url": "https://api.github.com/repos/ursa-labs/ursabot/assignees{/user}",
- "blobs_url": "https://api.github.com/repos/ursa-labs/ursabot/git/blobs{/sha}",
- "branches_url": "https://api.github.com/repos/ursa-labs/ursabot/branches{/branch}",
- "clone_url": "https://github.com/ursa-labs/ursabot.git",
- "collaborators_url": "https://api.github.com/repos/ursa-labs/ursabot/collaborators{/collaborator}",
- "comments_url": "https://api.github.com/repos/ursa-labs/ursabot/comments{/number}",
- "commits_url": "https://api.github.com/repos/ursa-labs/ursabot/commits{/sha}",
- "compare_url": "https://api.github.com/repos/ursa-labs/ursabot/compare/{base}...{head}",
- "contents_url": "https://api.github.com/repos/ursa-labs/ursabot/contents/{+path}",
- "contributors_url": "https://api.github.com/repos/ursa-labs/ursabot/contributors",
- "created_at": "2019-02-04T15:40:31Z",
- "default_branch": "master",
- "deployments_url": "https://api.github.com/repos/ursa-labs/ursabot/deployments",
- "description": null,
- "disabled": false,
- "downloads_url": "https://api.github.com/repos/ursa-labs/ursabot/downloads",
- "events_url": "https://api.github.com/repos/ursa-labs/ursabot/events",
- "fork": false,
- "forks": 0,
- "forks_count": 0,
- "forks_url": "https://api.github.com/repos/ursa-labs/ursabot/forks",
- "full_name": "ursa-labs/ursabot",
- "git_commits_url": "https://api.github.com/repos/ursa-labs/ursabot/git/commits{/sha}",
- "git_refs_url": "https://api.github.com/repos/ursa-labs/ursabot/git/refs{/sha}",
- "git_tags_url": "https://api.github.com/repos/ursa-labs/ursabot/git/tags{/sha}",
- "git_url": "git://github.com/ursa-labs/ursabot.git",
- "has_downloads": true,
- "has_issues": true,
- "has_pages": false,
- "has_projects": true,
- "has_wiki": true,
- "homepage": null,
- "hooks_url": "https://api.github.com/repos/ursa-labs/ursabot/hooks",
- "html_url": "https://github.com/ursa-labs/ursabot",
- "id": 169101701,
- "issue_comment_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/comments{/number}",
- "issue_events_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/events{/number}",
- "issues_url": "https://api.github.com/repos/ursa-labs/ursabot/issues{/number}",
- "keys_url": "https://api.github.com/repos/ursa-labs/ursabot/keys{/key_id}",
- "labels_url": "https://api.github.com/repos/ursa-labs/ursabot/labels{/name}",
- "language": "Jupyter Notebook",
- "languages_url": "https://api.github.com/repos/ursa-labs/ursabot/languages",
- "license": null,
- "merges_url": "https://api.github.com/repos/ursa-labs/ursabot/merges",
- "milestones_url": "https://api.github.com/repos/ursa-labs/ursabot/milestones{/number}",
- "mirror_url": null,
- "name": "ursabot",
- "node_id": "MDEwOlJlcG9zaXRvcnkxNjkxMDE3MDE=",
- "notifications_url": "https://api.github.com/repos/ursa-labs/ursabot/notifications{?since,all,participating}",
- "open_issues": 19,
- "open_issues_count": 19,
- "owner": {
- "avatar_url": "https://avatars2.githubusercontent.com/u/46514972?v=4",
- "events_url": "https://api.github.com/users/ursa-labs/events{/privacy}",
- "followers_url": "https://api.github.com/users/ursa-labs/followers",
- "following_url": "https://api.github.com/users/ursa-labs/following{/other_user}",
- "gists_url": "https://api.github.com/users/ursa-labs/gists{/gist_id}",
- "gravatar_id": "",
- "html_url": "https://github.com/ursa-labs",
- "id": 46514972,
- "login": "ursa-labs",
- "node_id": "MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy",
- "organizations_url": "https://api.github.com/users/ursa-labs/orgs",
- "received_events_url": "https://api.github.com/users/ursa-labs/received_events",
- "repos_url": "https://api.github.com/users/ursa-labs/repos",
- "site_admin": false,
- "starred_url": "https://api.github.com/users/ursa-labs/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/ursa-labs/subscriptions",
- "type": "Organization",
- "url": "https://api.github.com/users/ursa-labs"
- },
- "private": false,
- "pulls_url": "https://api.github.com/repos/ursa-labs/ursabot/pulls{/number}",
- "pushed_at": "2019-04-05T11:22:16Z",
- "releases_url": "https://api.github.com/repos/ursa-labs/ursabot/releases{/id}",
- "size": 892,
- "ssh_url": "git@github.com:ursa-labs/ursabot.git",
- "stargazers_count": 1,
- "stargazers_url": "https://api.github.com/repos/ursa-labs/ursabot/stargazers",
- "statuses_url": "https://api.github.com/repos/ursa-labs/ursabot/statuses/{sha}",
- "subscribers_url": "https://api.github.com/repos/ursa-labs/ursabot/subscribers",
- "subscription_url": "https://api.github.com/repos/ursa-labs/ursabot/subscription",
- "svn_url": "https://github.com/ursa-labs/ursabot",
- "tags_url": "https://api.github.com/repos/ursa-labs/ursabot/tags",
- "teams_url": "https://api.github.com/repos/ursa-labs/ursabot/teams",
- "trees_url": "https://api.github.com/repos/ursa-labs/ursabot/git/trees{/sha}",
- "updated_at": "2019-04-04T17:49:10Z",
- "url": "https://api.github.com/repos/ursa-labs/ursabot",
- "watchers": 1,
- "watchers_count": 1
- },
- "sender": {
- "avatar_url": "https://avatars1.githubusercontent.com/u/961747?v=4",
- "events_url": "https://api.github.com/users/kszucs/events{/privacy}",
- "followers_url": "https://api.github.com/users/kszucs/followers",
- "following_url": "https://api.github.com/users/kszucs/following{/other_user}",
- "gists_url": "https://api.github.com/users/kszucs/gists{/gist_id}",
- "gravatar_id": "",
- "html_url": "https://github.com/kszucs",
- "id": 961747,
- "login": "kszucs",
- "node_id": "MDQ6VXNlcjk2MTc0Nw==",
- "organizations_url": "https://api.github.com/users/kszucs/orgs",
- "received_events_url": "https://api.github.com/users/kszucs/received_events",
- "repos_url": "https://api.github.com/users/kszucs/repos",
- "site_admin": false,
- "starred_url": "https://api.github.com/users/kszucs/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/kszucs/subscriptions",
- "type": "User",
- "url": "https://api.github.com/users/kszucs"
- }
-}
\ No newline at end of file
diff --git a/dev/archery/archery/tests/fixtures/event-issue-comment-with-empty-command.json b/dev/archery/archery/tests/fixtures/event-issue-comment-with-empty-command.json
deleted file mode 100644
index c88197c..0000000
--- a/dev/archery/archery/tests/fixtures/event-issue-comment-with-empty-command.json
+++ /dev/null
@@ -1,217 +0,0 @@
-{
- "action": "created",
- "comment": {
- "author_association": "MEMBER",
- "body": "@ursabot ",
- "body_html": "",
- "body_text": "",
- "created_at": "2019-04-05T11:35:46Z",
- "html_url": "https://github.com/ursa-labs/ursabot/pull/26#issuecomment-480243811",
- "id": 480243811,
- "issue_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/26",
- "node_id": "MDEyOklzc3VlQ29tbWVudDQ4MDI0MzgxMQ==",
- "updated_at": "2019-04-05T11:35:46Z",
- "url": "https://api.github.com/repos/ursa-labs/ursabot/issues/comments/480243811",
- "user": {
- "avatar_url": "https://avatars1.githubusercontent.com/u/961747?v=4",
- "events_url": "https://api.github.com/users/kszucs/events{/privacy}",
- "followers_url": "https://api.github.com/users/kszucs/followers",
- "following_url": "https://api.github.com/users/kszucs/following{/other_user}",
- "gists_url": "https://api.github.com/users/kszucs/gists{/gist_id}",
- "gravatar_id": "",
- "html_url": "https://github.com/kszucs",
- "id": 961747,
- "login": "kszucs",
- "node_id": "MDQ6VXNlcjk2MTc0Nw==",
- "organizations_url": "https://api.github.com/users/kszucs/orgs",
- "received_events_url": "https://api.github.com/users/kszucs/received_events",
- "repos_url": "https://api.github.com/users/kszucs/repos",
- "site_admin": false,
- "starred_url": "https://api.github.com/users/kszucs/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/kszucs/subscriptions",
- "type": "User",
- "url": "https://api.github.com/users/kszucs"
- }
- },
- "issue": {
- "assignee": null,
- "assignees": [],
- "author_association": "MEMBER",
- "body": "",
- "body_html": "",
- "body_text": "",
- "closed_at": null,
- "closed_by": null,
- "comments": 1,
- "comments_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/26/comments",
- "created_at": "2019-04-05T11:22:15Z",
- "events_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/26/events",
- "html_url": "https://github.com/ursa-labs/ursabot/pull/26",
- "id": 429706959,
- "labels": [],
- "labels_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/26/labels{/name}",
- "locked": false,
- "milestone": null,
- "node_id": "MDExOlB1bGxSZXF1ZXN0MjY3Nzg1NTUy",
- "number": 26,
- "pull_request": {
- "diff_url": "https://github.com/ursa-labs/ursabot/pull/26.diff",
- "html_url": "https://github.com/ursa-labs/ursabot/pull/26",
- "patch_url": "https://github.com/ursa-labs/ursabot/pull/26.patch",
- "url": "https://api.github.com/repos/ursa-labs/ursabot/pulls/26"
- },
- "repository_url": "https://api.github.com/repos/ursa-labs/ursabot",
- "state": "open",
- "title": "Unittests for GithubHook",
- "updated_at": "2019-04-05T11:35:46Z",
- "url": "https://api.github.com/repos/ursa-labs/ursabot/issues/26",
- "user": {
- "avatar_url": "https://avatars1.githubusercontent.com/u/961747?v=4",
- "events_url": "https://api.github.com/users/kszucs/events{/privacy}",
- "followers_url": "https://api.github.com/users/kszucs/followers",
- "following_url": "https://api.github.com/users/kszucs/following{/other_user}",
- "gists_url": "https://api.github.com/users/kszucs/gists{/gist_id}",
- "gravatar_id": "",
- "html_url": "https://github.com/kszucs",
- "id": 961747,
- "login": "kszucs",
- "node_id": "MDQ6VXNlcjk2MTc0Nw==",
- "organizations_url": "https://api.github.com/users/kszucs/orgs",
- "received_events_url": "https://api.github.com/users/kszucs/received_events",
- "repos_url": "https://api.github.com/users/kszucs/repos",
- "site_admin": false,
- "starred_url": "https://api.github.com/users/kszucs/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/kszucs/subscriptions",
- "type": "User",
- "url": "https://api.github.com/users/kszucs"
- }
- },
- "organization": {
- "avatar_url": "https://avatars2.githubusercontent.com/u/46514972?v=4",
- "description": "Innovation lab for open source data science tools, powered by Apache Arrow",
- "events_url": "https://api.github.com/orgs/ursa-labs/events",
- "hooks_url": "https://api.github.com/orgs/ursa-labs/hooks",
- "id": 46514972,
- "issues_url": "https://api.github.com/orgs/ursa-labs/issues",
- "login": "ursa-labs",
- "members_url": "https://api.github.com/orgs/ursa-labs/members{/member}",
- "node_id": "MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy",
- "public_members_url": "https://api.github.com/orgs/ursa-labs/public_members{/member}",
- "repos_url": "https://api.github.com/orgs/ursa-labs/repos",
- "url": "https://api.github.com/orgs/ursa-labs"
- },
- "repository": {
- "archive_url": "https://api.github.com/repos/ursa-labs/ursabot/{archive_format}{/ref}",
- "archived": false,
- "assignees_url": "https://api.github.com/repos/ursa-labs/ursabot/assignees{/user}",
- "blobs_url": "https://api.github.com/repos/ursa-labs/ursabot/git/blobs{/sha}",
- "branches_url": "https://api.github.com/repos/ursa-labs/ursabot/branches{/branch}",
- "clone_url": "https://github.com/ursa-labs/ursabot.git",
- "collaborators_url": "https://api.github.com/repos/ursa-labs/ursabot/collaborators{/collaborator}",
- "comments_url": "https://api.github.com/repos/ursa-labs/ursabot/comments{/number}",
- "commits_url": "https://api.github.com/repos/ursa-labs/ursabot/commits{/sha}",
- "compare_url": "https://api.github.com/repos/ursa-labs/ursabot/compare/{base}...{head}",
- "contents_url": "https://api.github.com/repos/ursa-labs/ursabot/contents/{+path}",
- "contributors_url": "https://api.github.com/repos/ursa-labs/ursabot/contributors",
- "created_at": "2019-02-04T15:40:31Z",
- "default_branch": "master",
- "deployments_url": "https://api.github.com/repos/ursa-labs/ursabot/deployments",
- "description": null,
- "disabled": false,
- "downloads_url": "https://api.github.com/repos/ursa-labs/ursabot/downloads",
- "events_url": "https://api.github.com/repos/ursa-labs/ursabot/events",
- "fork": false,
- "forks": 0,
- "forks_count": 0,
- "forks_url": "https://api.github.com/repos/ursa-labs/ursabot/forks",
- "full_name": "ursa-labs/ursabot",
- "git_commits_url": "https://api.github.com/repos/ursa-labs/ursabot/git/commits{/sha}",
- "git_refs_url": "https://api.github.com/repos/ursa-labs/ursabot/git/refs{/sha}",
- "git_tags_url": "https://api.github.com/repos/ursa-labs/ursabot/git/tags{/sha}",
- "git_url": "git://github.com/ursa-labs/ursabot.git",
- "has_downloads": true,
- "has_issues": true,
- "has_pages": false,
- "has_projects": true,
- "has_wiki": true,
- "homepage": null,
- "hooks_url": "https://api.github.com/repos/ursa-labs/ursabot/hooks",
- "html_url": "https://github.com/ursa-labs/ursabot",
- "id": 169101701,
- "issue_comment_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/comments{/number}",
- "issue_events_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/events{/number}",
- "issues_url": "https://api.github.com/repos/ursa-labs/ursabot/issues{/number}",
- "keys_url": "https://api.github.com/repos/ursa-labs/ursabot/keys{/key_id}",
- "labels_url": "https://api.github.com/repos/ursa-labs/ursabot/labels{/name}",
- "language": "Jupyter Notebook",
- "languages_url": "https://api.github.com/repos/ursa-labs/ursabot/languages",
- "license": null,
- "merges_url": "https://api.github.com/repos/ursa-labs/ursabot/merges",
- "milestones_url": "https://api.github.com/repos/ursa-labs/ursabot/milestones{/number}",
- "mirror_url": null,
- "name": "ursabot",
- "node_id": "MDEwOlJlcG9zaXRvcnkxNjkxMDE3MDE=",
- "notifications_url": "https://api.github.com/repos/ursa-labs/ursabot/notifications{?since,all,participating}",
- "open_issues": 19,
- "open_issues_count": 19,
- "owner": {
- "avatar_url": "https://avatars2.githubusercontent.com/u/46514972?v=4",
- "events_url": "https://api.github.com/users/ursa-labs/events{/privacy}",
- "followers_url": "https://api.github.com/users/ursa-labs/followers",
- "following_url": "https://api.github.com/users/ursa-labs/following{/other_user}",
- "gists_url": "https://api.github.com/users/ursa-labs/gists{/gist_id}",
- "gravatar_id": "",
- "html_url": "https://github.com/ursa-labs",
- "id": 46514972,
- "login": "ursa-labs",
- "node_id": "MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy",
- "organizations_url": "https://api.github.com/users/ursa-labs/orgs",
- "received_events_url": "https://api.github.com/users/ursa-labs/received_events",
- "repos_url": "https://api.github.com/users/ursa-labs/repos",
- "site_admin": false,
- "starred_url": "https://api.github.com/users/ursa-labs/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/ursa-labs/subscriptions",
- "type": "Organization",
- "url": "https://api.github.com/users/ursa-labs"
- },
- "private": false,
- "pulls_url": "https://api.github.com/repos/ursa-labs/ursabot/pulls{/number}",
- "pushed_at": "2019-04-05T11:22:16Z",
- "releases_url": "https://api.github.com/repos/ursa-labs/ursabot/releases{/id}",
- "size": 892,
- "ssh_url": "git@github.com:ursa-labs/ursabot.git",
- "stargazers_count": 1,
- "stargazers_url": "https://api.github.com/repos/ursa-labs/ursabot/stargazers",
- "statuses_url": "https://api.github.com/repos/ursa-labs/ursabot/statuses/{sha}",
- "subscribers_url": "https://api.github.com/repos/ursa-labs/ursabot/subscribers",
- "subscription_url": "https://api.github.com/repos/ursa-labs/ursabot/subscription",
- "svn_url": "https://github.com/ursa-labs/ursabot",
- "tags_url": "https://api.github.com/repos/ursa-labs/ursabot/tags",
- "teams_url": "https://api.github.com/repos/ursa-labs/ursabot/teams",
- "trees_url": "https://api.github.com/repos/ursa-labs/ursabot/git/trees{/sha}",
- "updated_at": "2019-04-04T17:49:10Z",
- "url": "https://api.github.com/repos/ursa-labs/ursabot",
- "watchers": 1,
- "watchers_count": 1
- },
- "sender": {
- "avatar_url": "https://avatars1.githubusercontent.com/u/961747?v=4",
- "events_url": "https://api.github.com/users/kszucs/events{/privacy}",
- "followers_url": "https://api.github.com/users/kszucs/followers",
- "following_url": "https://api.github.com/users/kszucs/following{/other_user}",
- "gists_url": "https://api.github.com/users/kszucs/gists{/gist_id}",
- "gravatar_id": "",
- "html_url": "https://github.com/kszucs",
- "id": 961747,
- "login": "kszucs",
- "node_id": "MDQ6VXNlcjk2MTc0Nw==",
- "organizations_url": "https://api.github.com/users/kszucs/orgs",
- "received_events_url": "https://api.github.com/users/kszucs/received_events",
- "repos_url": "https://api.github.com/users/kszucs/repos",
- "site_admin": false,
- "starred_url": "https://api.github.com/users/kszucs/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/kszucs/subscriptions",
- "type": "User",
- "url": "https://api.github.com/users/kszucs"
- }
-}
\ No newline at end of file
diff --git a/dev/archery/archery/tests/fixtures/event-issue-comment-without-pull-request.json b/dev/archery/archery/tests/fixtures/event-issue-comment-without-pull-request.json
deleted file mode 100644
index 9e362fc..0000000
--- a/dev/archery/archery/tests/fixtures/event-issue-comment-without-pull-request.json
+++ /dev/null
@@ -1,206 +0,0 @@
-{
- "action": "created",
- "comment": {
- "author_association": "MEMBER",
- "body": "@ursabot build",
- "created_at": "2019-04-05T13:07:57Z",
- "html_url": "https://github.com/ursa-labs/ursabot/issues/19#issuecomment-480268708",
- "id": 480268708,
- "issue_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/19",
- "node_id": "MDEyOklzc3VlQ29tbWVudDQ4MDI2ODcwOA==",
- "updated_at": "2019-04-05T13:07:57Z",
- "url": "https://api.github.com/repos/ursa-labs/ursabot/issues/comments/480268708",
- "user": {
- "avatar_url": "https://avatars1.githubusercontent.com/u/961747?v=4",
- "events_url": "https://api.github.com/users/kszucs/events{/privacy}",
- "followers_url": "https://api.github.com/users/kszucs/followers",
- "following_url": "https://api.github.com/users/kszucs/following{/other_user}",
- "gists_url": "https://api.github.com/users/kszucs/gists{/gist_id}",
- "gravatar_id": "",
- "html_url": "https://github.com/kszucs",
- "id": 961747,
- "login": "kszucs",
- "node_id": "MDQ6VXNlcjk2MTc0Nw==",
- "organizations_url": "https://api.github.com/users/kszucs/orgs",
- "received_events_url": "https://api.github.com/users/kszucs/received_events",
- "repos_url": "https://api.github.com/users/kszucs/repos",
- "site_admin": false,
- "starred_url": "https://api.github.com/users/kszucs/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/kszucs/subscriptions",
- "type": "User",
- "url": "https://api.github.com/users/kszucs"
- }
- },
- "issue": {
- "assignee": null,
- "assignees": [],
- "author_association": "MEMBER",
- "body": "",
- "closed_at": null,
- "comments": 5,
- "comments_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/19/comments",
- "created_at": "2019-04-02T09:56:41Z",
- "events_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/19/events",
- "html_url": "https://github.com/ursa-labs/ursabot/issues/19",
- "id": 428131685,
- "labels": [],
- "labels_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/19/labels{/name}",
- "locked": false,
- "milestone": null,
- "node_id": "MDU6SXNzdWU0MjgxMzE2ODU=",
- "number": 19,
- "repository_url": "https://api.github.com/repos/ursa-labs/ursabot",
- "state": "open",
- "title": "Build ursabot itself via ursabot",
- "updated_at": "2019-04-05T13:07:57Z",
- "url": "https://api.github.com/repos/ursa-labs/ursabot/issues/19",
- "user": {
- "avatar_url": "https://avatars1.githubusercontent.com/u/961747?v=4",
- "events_url": "https://api.github.com/users/kszucs/events{/privacy}",
- "followers_url": "https://api.github.com/users/kszucs/followers",
- "following_url": "https://api.github.com/users/kszucs/following{/other_user}",
- "gists_url": "https://api.github.com/users/kszucs/gists{/gist_id}",
- "gravatar_id": "",
- "html_url": "https://github.com/kszucs",
- "id": 961747,
- "login": "kszucs",
- "node_id": "MDQ6VXNlcjk2MTc0Nw==",
- "organizations_url": "https://api.github.com/users/kszucs/orgs",
- "received_events_url": "https://api.github.com/users/kszucs/received_events",
- "repos_url": "https://api.github.com/users/kszucs/repos",
- "site_admin": false,
- "starred_url": "https://api.github.com/users/kszucs/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/kszucs/subscriptions",
- "type": "User",
- "url": "https://api.github.com/users/kszucs"
- }
- },
- "organization": {
- "avatar_url": "https://avatars2.githubusercontent.com/u/46514972?v=4",
- "description": "Innovation lab for open source data science tools, powered by Apache Arrow",
- "events_url": "https://api.github.com/orgs/ursa-labs/events",
- "hooks_url": "https://api.github.com/orgs/ursa-labs/hooks",
- "id": 46514972,
- "issues_url": "https://api.github.com/orgs/ursa-labs/issues",
- "login": "ursa-labs",
- "members_url": "https://api.github.com/orgs/ursa-labs/members{/member}",
- "node_id": "MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy",
- "public_members_url": "https://api.github.com/orgs/ursa-labs/public_members{/member}",
- "repos_url": "https://api.github.com/orgs/ursa-labs/repos",
- "url": "https://api.github.com/orgs/ursa-labs"
- },
- "repository": {
- "archive_url": "https://api.github.com/repos/ursa-labs/ursabot/{archive_format}{/ref}",
- "archived": false,
- "assignees_url": "https://api.github.com/repos/ursa-labs/ursabot/assignees{/user}",
- "blobs_url": "https://api.github.com/repos/ursa-labs/ursabot/git/blobs{/sha}",
- "branches_url": "https://api.github.com/repos/ursa-labs/ursabot/branches{/branch}",
- "clone_url": "https://github.com/ursa-labs/ursabot.git",
- "collaborators_url": "https://api.github.com/repos/ursa-labs/ursabot/collaborators{/collaborator}",
- "comments_url": "https://api.github.com/repos/ursa-labs/ursabot/comments{/number}",
- "commits_url": "https://api.github.com/repos/ursa-labs/ursabot/commits{/sha}",
- "compare_url": "https://api.github.com/repos/ursa-labs/ursabot/compare/{base}...{head}",
- "contents_url": "https://api.github.com/repos/ursa-labs/ursabot/contents/{+path}",
- "contributors_url": "https://api.github.com/repos/ursa-labs/ursabot/contributors",
- "created_at": "2019-02-04T15:40:31Z",
- "default_branch": "master",
- "deployments_url": "https://api.github.com/repos/ursa-labs/ursabot/deployments",
- "description": null,
- "disabled": false,
- "downloads_url": "https://api.github.com/repos/ursa-labs/ursabot/downloads",
- "events_url": "https://api.github.com/repos/ursa-labs/ursabot/events",
- "fork": false,
- "forks": 0,
- "forks_count": 0,
- "forks_url": "https://api.github.com/repos/ursa-labs/ursabot/forks",
- "full_name": "ursa-labs/ursabot",
- "git_commits_url": "https://api.github.com/repos/ursa-labs/ursabot/git/commits{/sha}",
- "git_refs_url": "https://api.github.com/repos/ursa-labs/ursabot/git/refs{/sha}",
- "git_tags_url": "https://api.github.com/repos/ursa-labs/ursabot/git/tags{/sha}",
- "git_url": "git://github.com/ursa-labs/ursabot.git",
- "has_downloads": true,
- "has_issues": true,
- "has_pages": false,
- "has_projects": true,
- "has_wiki": true,
- "homepage": null,
- "hooks_url": "https://api.github.com/repos/ursa-labs/ursabot/hooks",
- "html_url": "https://github.com/ursa-labs/ursabot",
- "id": 169101701,
- "issue_comment_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/comments{/number}",
- "issue_events_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/events{/number}",
- "issues_url": "https://api.github.com/repos/ursa-labs/ursabot/issues{/number}",
- "keys_url": "https://api.github.com/repos/ursa-labs/ursabot/keys{/key_id}",
- "labels_url": "https://api.github.com/repos/ursa-labs/ursabot/labels{/name}",
- "language": "Jupyter Notebook",
- "languages_url": "https://api.github.com/repos/ursa-labs/ursabot/languages",
- "license": null,
- "merges_url": "https://api.github.com/repos/ursa-labs/ursabot/merges",
- "milestones_url": "https://api.github.com/repos/ursa-labs/ursabot/milestones{/number}",
- "mirror_url": null,
- "name": "ursabot",
- "node_id": "MDEwOlJlcG9zaXRvcnkxNjkxMDE3MDE=",
- "notifications_url": "https://api.github.com/repos/ursa-labs/ursabot/notifications{?since,all,participating}",
- "open_issues": 19,
- "open_issues_count": 19,
- "owner": {
- "avatar_url": "https://avatars2.githubusercontent.com/u/46514972?v=4",
- "events_url": "https://api.github.com/users/ursa-labs/events{/privacy}",
- "followers_url": "https://api.github.com/users/ursa-labs/followers",
- "following_url": "https://api.github.com/users/ursa-labs/following{/other_user}",
- "gists_url": "https://api.github.com/users/ursa-labs/gists{/gist_id}",
- "gravatar_id": "",
- "html_url": "https://github.com/ursa-labs",
- "id": 46514972,
- "login": "ursa-labs",
- "node_id": "MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy",
- "organizations_url": "https://api.github.com/users/ursa-labs/orgs",
- "received_events_url": "https://api.github.com/users/ursa-labs/received_events",
- "repos_url": "https://api.github.com/users/ursa-labs/repos",
- "site_admin": false,
- "starred_url": "https://api.github.com/users/ursa-labs/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/ursa-labs/subscriptions",
- "type": "Organization",
- "url": "https://api.github.com/users/ursa-labs"
- },
- "private": false,
- "pulls_url": "https://api.github.com/repos/ursa-labs/ursabot/pulls{/number}",
- "pushed_at": "2019-04-05T12:01:40Z",
- "releases_url": "https://api.github.com/repos/ursa-labs/ursabot/releases{/id}",
- "size": 898,
- "ssh_url": "git@github.com:ursa-labs/ursabot.git",
- "stargazers_count": 1,
- "stargazers_url": "https://api.github.com/repos/ursa-labs/ursabot/stargazers",
- "statuses_url": "https://api.github.com/repos/ursa-labs/ursabot/statuses/{sha}",
- "subscribers_url": "https://api.github.com/repos/ursa-labs/ursabot/subscribers",
- "subscription_url": "https://api.github.com/repos/ursa-labs/ursabot/subscription",
- "svn_url": "https://github.com/ursa-labs/ursabot",
- "tags_url": "https://api.github.com/repos/ursa-labs/ursabot/tags",
- "teams_url": "https://api.github.com/repos/ursa-labs/ursabot/teams",
- "trees_url": "https://api.github.com/repos/ursa-labs/ursabot/git/trees{/sha}",
- "updated_at": "2019-04-04T17:49:10Z",
- "url": "https://api.github.com/repos/ursa-labs/ursabot",
- "watchers": 1,
- "watchers_count": 1
- },
- "sender": {
- "avatar_url": "https://avatars1.githubusercontent.com/u/961747?v=4",
- "events_url": "https://api.github.com/users/kszucs/events{/privacy}",
- "followers_url": "https://api.github.com/users/kszucs/followers",
- "following_url": "https://api.github.com/users/kszucs/following{/other_user}",
- "gists_url": "https://api.github.com/users/kszucs/gists{/gist_id}",
- "gravatar_id": "",
- "html_url": "https://github.com/kszucs",
- "id": 961747,
- "login": "kszucs",
- "node_id": "MDQ6VXNlcjk2MTc0Nw==",
- "organizations_url": "https://api.github.com/users/kszucs/orgs",
- "received_events_url": "https://api.github.com/users/kszucs/received_events",
- "repos_url": "https://api.github.com/users/kszucs/repos",
- "site_admin": false,
- "starred_url": "https://api.github.com/users/kszucs/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/kszucs/subscriptions",
- "type": "User",
- "url": "https://api.github.com/users/kszucs"
- }
-}
\ No newline at end of file
diff --git a/dev/archery/archery/tests/fixtures/event-pull-request-opened.json b/dev/archery/archery/tests/fixtures/event-pull-request-opened.json
deleted file mode 100644
index 9cf5c0d..0000000
--- a/dev/archery/archery/tests/fixtures/event-pull-request-opened.json
+++ /dev/null
@@ -1,445 +0,0 @@
-{
- "action": "opened",
- "number": 26,
- "pull_request": {
- "url": "https://api.github.com/repos/ursa-labs/ursabot/pulls/26",
- "id": 267785552,
- "node_id": "MDExOlB1bGxSZXF1ZXN0MjY3Nzg1NTUy",
- "html_url": "https://github.com/ursa-labs/ursabot/pull/26",
- "diff_url": "https://github.com/ursa-labs/ursabot/pull/26.diff",
- "patch_url": "https://github.com/ursa-labs/ursabot/pull/26.patch",
- "issue_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/26",
- "number": 26,
- "state": "open",
- "locked": false,
- "title": "Unittests for GithubHook",
- "user": {
- "login": "kszucs",
- "id": 961747,
- "node_id": "MDQ6VXNlcjk2MTc0Nw==",
- "avatar_url": "https://avatars1.githubusercontent.com/u/961747?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/kszucs",
- "html_url": "https://github.com/kszucs",
- "followers_url": "https://api.github.com/users/kszucs/followers",
- "following_url": "https://api.github.com/users/kszucs/following{/other_user}",
- "gists_url": "https://api.github.com/users/kszucs/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/kszucs/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/kszucs/subscriptions",
- "organizations_url": "https://api.github.com/users/kszucs/orgs",
- "repos_url": "https://api.github.com/users/kszucs/repos",
- "events_url": "https://api.github.com/users/kszucs/events{/privacy}",
- "received_events_url": "https://api.github.com/users/kszucs/received_events",
- "type": "User",
- "site_admin": false
- },
- "body": "",
- "created_at": "2019-04-05T11:22:15Z",
- "updated_at": "2019-04-05T12:01:40Z",
- "closed_at": null,
- "merged_at": null,
- "merge_commit_sha": "cc5dc3606988b3824be54df779ed2028776113cb",
- "assignee": null,
- "assignees": [],
- "requested_reviewers": [],
- "requested_teams": [],
- "labels": [],
- "milestone": null,
- "commits_url": "https://api.github.com/repos/ursa-labs/ursabot/pulls/26/commits",
- "review_comments_url": "https://api.github.com/repos/ursa-labs/ursabot/pulls/26/comments",
- "review_comment_url": "https://api.github.com/repos/ursa-labs/ursabot/pulls/comments{/number}",
- "comments_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/26/comments",
- "statuses_url": "https://api.github.com/repos/ursa-labs/ursabot/statuses/2705da2b616b98fa6010a25813c5a7a27456f71d",
- "head": {
- "label": "ursa-labs:test-hook",
- "ref": "test-hook",
- "sha": "2705da2b616b98fa6010a25813c5a7a27456f71d",
- "user": {
- "login": "ursa-labs",
- "id": 46514972,
- "node_id": "MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy",
- "avatar_url": "https://avatars2.githubusercontent.com/u/46514972?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/ursa-labs",
- "html_url": "https://github.com/ursa-labs",
- "followers_url": "https://api.github.com/users/ursa-labs/followers",
- "following_url": "https://api.github.com/users/ursa-labs/following{/other_user}",
- "gists_url": "https://api.github.com/users/ursa-labs/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/ursa-labs/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/ursa-labs/subscriptions",
- "organizations_url": "https://api.github.com/users/ursa-labs/orgs",
- "repos_url": "https://api.github.com/users/ursa-labs/repos",
- "events_url": "https://api.github.com/users/ursa-labs/events{/privacy}",
- "received_events_url": "https://api.github.com/users/ursa-labs/received_events",
- "type": "Organization",
- "site_admin": false
- },
- "repo": {
- "id": 169101701,
- "node_id": "MDEwOlJlcG9zaXRvcnkxNjkxMDE3MDE=",
- "name": "ursabot",
- "full_name": "ursa-labs/ursabot",
- "private": false,
- "owner": {
- "login": "ursa-labs",
- "id": 46514972,
- "node_id": "MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy",
- "avatar_url": "https://avatars2.githubusercontent.com/u/46514972?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/ursa-labs",
- "html_url": "https://github.com/ursa-labs",
- "followers_url": "https://api.github.com/users/ursa-labs/followers",
- "following_url": "https://api.github.com/users/ursa-labs/following{/other_user}",
- "gists_url": "https://api.github.com/users/ursa-labs/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/ursa-labs/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/ursa-labs/subscriptions",
- "organizations_url": "https://api.github.com/users/ursa-labs/orgs",
- "repos_url": "https://api.github.com/users/ursa-labs/repos",
- "events_url": "https://api.github.com/users/ursa-labs/events{/privacy}",
- "received_events_url": "https://api.github.com/users/ursa-labs/received_events",
- "type": "Organization",
- "site_admin": false
- },
- "html_url": "https://github.com/ursa-labs/ursabot",
- "description": null,
- "fork": false,
- "url": "https://api.github.com/repos/ursa-labs/ursabot",
- "forks_url": "https://api.github.com/repos/ursa-labs/ursabot/forks",
- "keys_url": "https://api.github.com/repos/ursa-labs/ursabot/keys{/key_id}",
- "collaborators_url": "https://api.github.com/repos/ursa-labs/ursabot/collaborators{/collaborator}",
- "teams_url": "https://api.github.com/repos/ursa-labs/ursabot/teams",
- "hooks_url": "https://api.github.com/repos/ursa-labs/ursabot/hooks",
- "issue_events_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/events{/number}",
- "events_url": "https://api.github.com/repos/ursa-labs/ursabot/events",
- "assignees_url": "https://api.github.com/repos/ursa-labs/ursabot/assignees{/user}",
- "branches_url": "https://api.github.com/repos/ursa-labs/ursabot/branches{/branch}",
- "tags_url": "https://api.github.com/repos/ursa-labs/ursabot/tags",
- "blobs_url": "https://api.github.com/repos/ursa-labs/ursabot/git/blobs{/sha}",
- "git_tags_url": "https://api.github.com/repos/ursa-labs/ursabot/git/tags{/sha}",
- "git_refs_url": "https://api.github.com/repos/ursa-labs/ursabot/git/refs{/sha}",
- "trees_url": "https://api.github.com/repos/ursa-labs/ursabot/git/trees{/sha}",
- "statuses_url": "https://api.github.com/repos/ursa-labs/ursabot/statuses/{sha}",
- "languages_url": "https://api.github.com/repos/ursa-labs/ursabot/languages",
- "stargazers_url": "https://api.github.com/repos/ursa-labs/ursabot/stargazers",
- "contributors_url": "https://api.github.com/repos/ursa-labs/ursabot/contributors",
- "subscribers_url": "https://api.github.com/repos/ursa-labs/ursabot/subscribers",
- "subscription_url": "https://api.github.com/repos/ursa-labs/ursabot/subscription",
- "commits_url": "https://api.github.com/repos/ursa-labs/ursabot/commits{/sha}",
- "git_commits_url": "https://api.github.com/repos/ursa-labs/ursabot/git/commits{/sha}",
- "comments_url": "https://api.github.com/repos/ursa-labs/ursabot/comments{/number}",
- "issue_comment_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/comments{/number}",
- "contents_url": "https://api.github.com/repos/ursa-labs/ursabot/contents/{+path}",
- "compare_url": "https://api.github.com/repos/ursa-labs/ursabot/compare/{base}...{head}",
- "merges_url": "https://api.github.com/repos/ursa-labs/ursabot/merges",
- "archive_url": "https://api.github.com/repos/ursa-labs/ursabot/{archive_format}{/ref}",
- "downloads_url": "https://api.github.com/repos/ursa-labs/ursabot/downloads",
- "issues_url": "https://api.github.com/repos/ursa-labs/ursabot/issues{/number}",
- "pulls_url": "https://api.github.com/repos/ursa-labs/ursabot/pulls{/number}",
- "milestones_url": "https://api.github.com/repos/ursa-labs/ursabot/milestones{/number}",
- "notifications_url": "https://api.github.com/repos/ursa-labs/ursabot/notifications{?since,all,participating}",
- "labels_url": "https://api.github.com/repos/ursa-labs/ursabot/labels{/name}",
- "releases_url": "https://api.github.com/repos/ursa-labs/ursabot/releases{/id}",
- "deployments_url": "https://api.github.com/repos/ursa-labs/ursabot/deployments",
- "created_at": "2019-02-04T15:40:31Z",
- "updated_at": "2019-04-04T17:49:10Z",
- "pushed_at": "2019-04-05T12:01:40Z",
- "git_url": "git://github.com/ursa-labs/ursabot.git",
- "ssh_url": "git@github.com:ursa-labs/ursabot.git",
- "clone_url": "https://github.com/ursa-labs/ursabot.git",
- "svn_url": "https://github.com/ursa-labs/ursabot",
- "homepage": null,
- "size": 898,
- "stargazers_count": 1,
- "watchers_count": 1,
- "language": "Jupyter Notebook",
- "has_issues": true,
- "has_projects": true,
- "has_downloads": true,
- "has_wiki": true,
- "has_pages": false,
- "forks_count": 0,
- "mirror_url": null,
- "archived": false,
- "disabled": false,
- "open_issues_count": 19,
- "license": null,
- "forks": 0,
- "open_issues": 19,
- "watchers": 1,
- "default_branch": "master"
- }
- },
- "base": {
- "label": "ursa-labs:master",
- "ref": "master",
- "sha": "a162ad254b589b924db47e057791191b39613fd5",
- "user": {
- "login": "ursa-labs",
- "id": 46514972,
- "node_id": "MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy",
- "avatar_url": "https://avatars2.githubusercontent.com/u/46514972?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/ursa-labs",
- "html_url": "https://github.com/ursa-labs",
- "followers_url": "https://api.github.com/users/ursa-labs/followers",
- "following_url": "https://api.github.com/users/ursa-labs/following{/other_user}",
- "gists_url": "https://api.github.com/users/ursa-labs/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/ursa-labs/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/ursa-labs/subscriptions",
- "organizations_url": "https://api.github.com/users/ursa-labs/orgs",
- "repos_url": "https://api.github.com/users/ursa-labs/repos",
- "events_url": "https://api.github.com/users/ursa-labs/events{/privacy}",
- "received_events_url": "https://api.github.com/users/ursa-labs/received_events",
- "type": "Organization",
- "site_admin": false
- },
- "repo": {
- "id": 169101701,
- "node_id": "MDEwOlJlcG9zaXRvcnkxNjkxMDE3MDE=",
- "name": "ursabot",
- "full_name": "ursa-labs/ursabot",
- "private": false,
- "owner": {
- "login": "ursa-labs",
- "id": 46514972,
- "node_id": "MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy",
- "avatar_url": "https://avatars2.githubusercontent.com/u/46514972?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/ursa-labs",
- "html_url": "https://github.com/ursa-labs",
- "followers_url": "https://api.github.com/users/ursa-labs/followers",
- "following_url": "https://api.github.com/users/ursa-labs/following{/other_user}",
- "gists_url": "https://api.github.com/users/ursa-labs/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/ursa-labs/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/ursa-labs/subscriptions",
- "organizations_url": "https://api.github.com/users/ursa-labs/orgs",
- "repos_url": "https://api.github.com/users/ursa-labs/repos",
- "events_url": "https://api.github.com/users/ursa-labs/events{/privacy}",
- "received_events_url": "https://api.github.com/users/ursa-labs/received_events",
- "type": "Organization",
- "site_admin": false
- },
- "html_url": "https://github.com/ursa-labs/ursabot",
- "description": null,
- "fork": false,
- "url": "https://api.github.com/repos/ursa-labs/ursabot",
- "forks_url": "https://api.github.com/repos/ursa-labs/ursabot/forks",
- "keys_url": "https://api.github.com/repos/ursa-labs/ursabot/keys{/key_id}",
- "collaborators_url": "https://api.github.com/repos/ursa-labs/ursabot/collaborators{/collaborator}",
- "teams_url": "https://api.github.com/repos/ursa-labs/ursabot/teams",
- "hooks_url": "https://api.github.com/repos/ursa-labs/ursabot/hooks",
- "issue_events_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/events{/number}",
- "events_url": "https://api.github.com/repos/ursa-labs/ursabot/events",
- "assignees_url": "https://api.github.com/repos/ursa-labs/ursabot/assignees{/user}",
- "branches_url": "https://api.github.com/repos/ursa-labs/ursabot/branches{/branch}",
- "tags_url": "https://api.github.com/repos/ursa-labs/ursabot/tags",
- "blobs_url": "https://api.github.com/repos/ursa-labs/ursabot/git/blobs{/sha}",
- "git_tags_url": "https://api.github.com/repos/ursa-labs/ursabot/git/tags{/sha}",
- "git_refs_url": "https://api.github.com/repos/ursa-labs/ursabot/git/refs{/sha}",
- "trees_url": "https://api.github.com/repos/ursa-labs/ursabot/git/trees{/sha}",
- "statuses_url": "https://api.github.com/repos/ursa-labs/ursabot/statuses/{sha}",
- "languages_url": "https://api.github.com/repos/ursa-labs/ursabot/languages",
- "stargazers_url": "https://api.github.com/repos/ursa-labs/ursabot/stargazers",
- "contributors_url": "https://api.github.com/repos/ursa-labs/ursabot/contributors",
- "subscribers_url": "https://api.github.com/repos/ursa-labs/ursabot/subscribers",
- "subscription_url": "https://api.github.com/repos/ursa-labs/ursabot/subscription",
- "commits_url": "https://api.github.com/repos/ursa-labs/ursabot/commits{/sha}",
- "git_commits_url": "https://api.github.com/repos/ursa-labs/ursabot/git/commits{/sha}",
- "comments_url": "https://api.github.com/repos/ursa-labs/ursabot/comments{/number}",
- "issue_comment_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/comments{/number}",
- "contents_url": "https://api.github.com/repos/ursa-labs/ursabot/contents/{+path}",
- "compare_url": "https://api.github.com/repos/ursa-labs/ursabot/compare/{base}...{head}",
- "merges_url": "https://api.github.com/repos/ursa-labs/ursabot/merges",
- "archive_url": "https://api.github.com/repos/ursa-labs/ursabot/{archive_format}{/ref}",
- "downloads_url": "https://api.github.com/repos/ursa-labs/ursabot/downloads",
- "issues_url": "https://api.github.com/repos/ursa-labs/ursabot/issues{/number}",
- "pulls_url": "https://api.github.com/repos/ursa-labs/ursabot/pulls{/number}",
- "milestones_url": "https://api.github.com/repos/ursa-labs/ursabot/milestones{/number}",
- "notifications_url": "https://api.github.com/repos/ursa-labs/ursabot/notifications{?since,all,participating}",
- "labels_url": "https://api.github.com/repos/ursa-labs/ursabot/labels{/name}",
- "releases_url": "https://api.github.com/repos/ursa-labs/ursabot/releases{/id}",
- "deployments_url": "https://api.github.com/repos/ursa-labs/ursabot/deployments",
- "created_at": "2019-02-04T15:40:31Z",
- "updated_at": "2019-04-04T17:49:10Z",
- "pushed_at": "2019-04-05T12:01:40Z",
- "git_url": "git://github.com/ursa-labs/ursabot.git",
- "ssh_url": "git@github.com:ursa-labs/ursabot.git",
- "clone_url": "https://github.com/ursa-labs/ursabot.git",
- "svn_url": "https://github.com/ursa-labs/ursabot",
- "homepage": null,
- "size": 898,
- "stargazers_count": 1,
- "watchers_count": 1,
- "language": "Jupyter Notebook",
- "has_issues": true,
- "has_projects": true,
- "has_downloads": true,
- "has_wiki": true,
- "has_pages": false,
- "forks_count": 0,
- "mirror_url": null,
- "archived": false,
- "disabled": false,
- "open_issues_count": 19,
- "license": null,
- "forks": 0,
- "open_issues": 19,
- "watchers": 1,
- "default_branch": "master"
- }
- },
- "_links": {
- "self": {
- "href": "https://api.github.com/repos/ursa-labs/ursabot/pulls/26"
- },
- "html": {
- "href": "https://github.com/ursa-labs/ursabot/pull/26"
- },
- "issue": {
- "href": "https://api.github.com/repos/ursa-labs/ursabot/issues/26"
- },
- "comments": {
- "href": "https://api.github.com/repos/ursa-labs/ursabot/issues/26/comments"
- },
- "review_comments": {
- "href": "https://api.github.com/repos/ursa-labs/ursabot/pulls/26/comments"
- },
- "review_comment": {
- "href": "https://api.github.com/repos/ursa-labs/ursabot/pulls/comments{/number}"
- },
- "commits": {
- "href": "https://api.github.com/repos/ursa-labs/ursabot/pulls/26/commits"
- },
- "statuses": {
- "href": "https://api.github.com/repos/ursa-labs/ursabot/statuses/2705da2b616b98fa6010a25813c5a7a27456f71d"
- }
- },
- "author_association": "MEMBER",
- "merged": false,
- "mergeable": true,
- "rebaseable": true,
- "mergeable_state": "unstable",
- "merged_by": null,
- "comments": 5,
- "review_comments": 0,
- "maintainer_can_modify": false,
- "commits": 2,
- "additions": 1124,
- "deletions": 0,
- "changed_files": 7
- },
- "repository": {
- "archive_url": "https://api.github.com/repos/ursa-labs/ursabot/{archive_format}{/ref}",
- "archived": false,
- "assignees_url": "https://api.github.com/repos/ursa-labs/ursabot/assignees{/user}",
- "blobs_url": "https://api.github.com/repos/ursa-labs/ursabot/git/blobs{/sha}",
- "branches_url": "https://api.github.com/repos/ursa-labs/ursabot/branches{/branch}",
- "clone_url": "https://github.com/ursa-labs/ursabot.git",
- "collaborators_url": "https://api.github.com/repos/ursa-labs/ursabot/collaborators{/collaborator}",
- "comments_url": "https://api.github.com/repos/ursa-labs/ursabot/comments{/number}",
- "commits_url": "https://api.github.com/repos/ursa-labs/ursabot/commits{/sha}",
- "compare_url": "https://api.github.com/repos/ursa-labs/ursabot/compare/{base}...{head}",
- "contents_url": "https://api.github.com/repos/ursa-labs/ursabot/contents/{+path}",
- "contributors_url": "https://api.github.com/repos/ursa-labs/ursabot/contributors",
- "created_at": "2019-02-04T15:40:31Z",
- "default_branch": "master",
- "deployments_url": "https://api.github.com/repos/ursa-labs/ursabot/deployments",
- "description": null,
- "disabled": false,
- "downloads_url": "https://api.github.com/repos/ursa-labs/ursabot/downloads",
- "events_url": "https://api.github.com/repos/ursa-labs/ursabot/events",
- "fork": false,
- "forks": 0,
- "forks_count": 0,
- "forks_url": "https://api.github.com/repos/ursa-labs/ursabot/forks",
- "full_name": "ursa-labs/ursabot",
- "git_commits_url": "https://api.github.com/repos/ursa-labs/ursabot/git/commits{/sha}",
- "git_refs_url": "https://api.github.com/repos/ursa-labs/ursabot/git/refs{/sha}",
- "git_tags_url": "https://api.github.com/repos/ursa-labs/ursabot/git/tags{/sha}",
- "git_url": "git://github.com/ursa-labs/ursabot.git",
- "has_downloads": true,
- "has_issues": true,
- "has_pages": false,
- "has_projects": true,
- "has_wiki": true,
- "homepage": null,
- "hooks_url": "https://api.github.com/repos/ursa-labs/ursabot/hooks",
- "html_url": "https://github.com/ursa-labs/ursabot",
- "id": 169101701,
- "issue_comment_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/comments{/number}",
- "issue_events_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/events{/number}",
- "issues_url": "https://api.github.com/repos/ursa-labs/ursabot/issues{/number}",
- "keys_url": "https://api.github.com/repos/ursa-labs/ursabot/keys{/key_id}",
- "labels_url": "https://api.github.com/repos/ursa-labs/ursabot/labels{/name}",
- "language": "Jupyter Notebook",
- "languages_url": "https://api.github.com/repos/ursa-labs/ursabot/languages",
- "license": null,
- "merges_url": "https://api.github.com/repos/ursa-labs/ursabot/merges",
- "milestones_url": "https://api.github.com/repos/ursa-labs/ursabot/milestones{/number}",
- "mirror_url": null,
- "name": "ursabot",
- "node_id": "MDEwOlJlcG9zaXRvcnkxNjkxMDE3MDE=",
- "notifications_url": "https://api.github.com/repos/ursa-labs/ursabot/notifications{?since,all,participating}",
- "open_issues": 19,
- "open_issues_count": 19,
- "owner": {
- "avatar_url": "https://avatars2.githubusercontent.com/u/46514972?v=4",
- "events_url": "https://api.github.com/users/ursa-labs/events{/privacy}",
- "followers_url": "https://api.github.com/users/ursa-labs/followers",
- "following_url": "https://api.github.com/users/ursa-labs/following{/other_user}",
- "gists_url": "https://api.github.com/users/ursa-labs/gists{/gist_id}",
- "gravatar_id": "",
- "html_url": "https://github.com/ursa-labs",
- "id": 46514972,
- "login": "ursa-labs",
- "node_id": "MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy",
- "organizations_url": "https://api.github.com/users/ursa-labs/orgs",
- "received_events_url": "https://api.github.com/users/ursa-labs/received_events",
- "repos_url": "https://api.github.com/users/ursa-labs/repos",
- "site_admin": false,
- "starred_url": "https://api.github.com/users/ursa-labs/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/ursa-labs/subscriptions",
- "type": "Organization",
- "url": "https://api.github.com/users/ursa-labs"
- },
- "private": false,
- "pulls_url": "https://api.github.com/repos/ursa-labs/ursabot/pulls{/number}",
- "pushed_at": "2019-04-05T11:22:16Z",
- "releases_url": "https://api.github.com/repos/ursa-labs/ursabot/releases{/id}",
- "size": 892,
- "ssh_url": "git@github.com:ursa-labs/ursabot.git",
- "stargazers_count": 1,
- "stargazers_url": "https://api.github.com/repos/ursa-labs/ursabot/stargazers",
- "statuses_url": "https://api.github.com/repos/ursa-labs/ursabot/statuses/{sha}",
- "subscribers_url": "https://api.github.com/repos/ursa-labs/ursabot/subscribers",
- "subscription_url": "https://api.github.com/repos/ursa-labs/ursabot/subscription",
- "svn_url": "https://github.com/ursa-labs/ursabot",
- "tags_url": "https://api.github.com/repos/ursa-labs/ursabot/tags",
- "teams_url": "https://api.github.com/repos/ursa-labs/ursabot/teams",
- "trees_url": "https://api.github.com/repos/ursa-labs/ursabot/git/trees{/sha}",
- "updated_at": "2019-04-04T17:49:10Z",
- "url": "https://api.github.com/repos/ursa-labs/ursabot",
- "watchers": 1,
- "watchers_count": 1
- },
- "sender": {
- "avatar_url": "https://avatars1.githubusercontent.com/u/961747?v=4",
- "events_url": "https://api.github.com/users/kszucs/events{/privacy}",
- "followers_url": "https://api.github.com/users/kszucs/followers",
- "following_url": "https://api.github.com/users/kszucs/following{/other_user}",
- "gists_url": "https://api.github.com/users/kszucs/gists{/gist_id}",
- "gravatar_id": "",
- "html_url": "https://github.com/kszucs",
- "id": 961747,
- "login": "kszucs",
- "node_id": "MDQ6VXNlcjk2MTc0Nw==",
- "organizations_url": "https://api.github.com/users/kszucs/orgs",
- "received_events_url": "https://api.github.com/users/kszucs/received_events",
- "repos_url": "https://api.github.com/users/kszucs/repos",
- "site_admin": false,
- "starred_url": "https://api.github.com/users/kszucs/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/kszucs/subscriptions",
- "type": "User",
- "url": "https://api.github.com/users/kszucs"
- }
-}
\ No newline at end of file
diff --git a/dev/archery/archery/tests/fixtures/issue-19.json b/dev/archery/archery/tests/fixtures/issue-19.json
deleted file mode 100644
index 1e49397..0000000
--- a/dev/archery/archery/tests/fixtures/issue-19.json
+++ /dev/null
@@ -1,64 +0,0 @@
-{
- "url": "https://api.github.com/repos/ursa-labs/ursabot/issues/19",
- "repository_url": "https://api.github.com/repos/ursa-labs/ursabot",
- "labels_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/19/labels{/name}",
- "comments_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/19/comments",
- "events_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/19/events",
- "html_url": "https://github.com/ursa-labs/ursabot/issues/19",
- "id": 428131685,
- "node_id": "MDU6SXNzdWU0MjgxMzE2ODU=",
- "number": 19,
- "title": "Build ursabot itself via ursabot",
- "user": {
- "login": "kszucs",
- "id": 961747,
- "node_id": "MDQ6VXNlcjk2MTc0Nw==",
- "avatar_url": "https://avatars1.githubusercontent.com/u/961747?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/kszucs",
- "html_url": "https://github.com/kszucs",
- "followers_url": "https://api.github.com/users/kszucs/followers",
- "following_url": "https://api.github.com/users/kszucs/following{/other_user}",
- "gists_url": "https://api.github.com/users/kszucs/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/kszucs/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/kszucs/subscriptions",
- "organizations_url": "https://api.github.com/users/kszucs/orgs",
- "repos_url": "https://api.github.com/users/kszucs/repos",
- "events_url": "https://api.github.com/users/kszucs/events{/privacy}",
- "received_events_url": "https://api.github.com/users/kszucs/received_events",
- "type": "User",
- "site_admin": false
- },
- "labels": [],
- "state": "closed",
- "locked": false,
- "assignee": null,
- "assignees": [],
- "milestone": null,
- "comments": 8,
- "created_at": "2019-04-02T09:56:41Z",
- "updated_at": "2019-04-05T13:30:49Z",
- "closed_at": "2019-04-05T13:30:49Z",
- "author_association": "MEMBER",
- "body": "",
- "closed_by": {
- "login": "kszucs",
- "id": 961747,
- "node_id": "MDQ6VXNlcjk2MTc0Nw==",
- "avatar_url": "https://avatars1.githubusercontent.com/u/961747?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/kszucs",
- "html_url": "https://github.com/kszucs",
- "followers_url": "https://api.github.com/users/kszucs/followers",
- "following_url": "https://api.github.com/users/kszucs/following{/other_user}",
- "gists_url": "https://api.github.com/users/kszucs/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/kszucs/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/kszucs/subscriptions",
- "organizations_url": "https://api.github.com/users/kszucs/orgs",
- "repos_url": "https://api.github.com/users/kszucs/repos",
- "events_url": "https://api.github.com/users/kszucs/events{/privacy}",
- "received_events_url": "https://api.github.com/users/kszucs/received_events",
- "type": "User",
- "site_admin": false
- }
-}
\ No newline at end of file
diff --git a/dev/archery/archery/tests/fixtures/issue-26.json b/dev/archery/archery/tests/fixtures/issue-26.json
deleted file mode 100644
index 44c4d3b..0000000
--- a/dev/archery/archery/tests/fixtures/issue-26.json
+++ /dev/null
@@ -1,70 +0,0 @@
-{
- "url": "https://api.github.com/repos/ursa-labs/ursabot/issues/26",
- "repository_url": "https://api.github.com/repos/ursa-labs/ursabot",
- "labels_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/26/labels{/name}",
- "comments_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/26/comments",
- "events_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/26/events",
- "html_url": "https://github.com/ursa-labs/ursabot/pull/26",
- "id": 429706959,
- "node_id": "MDExOlB1bGxSZXF1ZXN0MjY3Nzg1NTUy",
- "number": 26,
- "title": "Unittests for GithubHook + native asyncio syntax",
- "user": {
- "login": "kszucs",
- "id": 961747,
- "node_id": "MDQ6VXNlcjk2MTc0Nw==",
- "avatar_url": "https://avatars1.githubusercontent.com/u/961747?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/kszucs",
- "html_url": "https://github.com/kszucs",
- "followers_url": "https://api.github.com/users/kszucs/followers",
- "following_url": "https://api.github.com/users/kszucs/following{/other_user}",
- "gists_url": "https://api.github.com/users/kszucs/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/kszucs/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/kszucs/subscriptions",
- "organizations_url": "https://api.github.com/users/kszucs/orgs",
- "repos_url": "https://api.github.com/users/kszucs/repos",
- "events_url": "https://api.github.com/users/kszucs/events{/privacy}",
- "received_events_url": "https://api.github.com/users/kszucs/received_events",
- "type": "User",
- "site_admin": false
- },
- "labels": [],
- "state": "closed",
- "locked": false,
- "assignee": null,
- "assignees": [],
- "milestone": null,
- "comments": 9,
- "created_at": "2019-04-05T11:22:15Z",
- "updated_at": "2019-08-28T00:34:19Z",
- "closed_at": "2019-04-05T13:54:34Z",
- "author_association": "MEMBER",
- "pull_request": {
- "url": "https://api.github.com/repos/ursa-labs/ursabot/pulls/26",
- "html_url": "https://github.com/ursa-labs/ursabot/pull/26",
- "diff_url": "https://github.com/ursa-labs/ursabot/pull/26.diff",
- "patch_url": "https://github.com/ursa-labs/ursabot/pull/26.patch"
- },
- "body": "Resolves:\r\n- #26 Unittests for GithubHook + native asyncio syntax\r\n- #27 Use native async/await keywords instead of @inlineCallbacks and yield\r\n",
- "closed_by": {
- "login": "kszucs",
- "id": 961747,
- "node_id": "MDQ6VXNlcjk2MTc0Nw==",
- "avatar_url": "https://avatars1.githubusercontent.com/u/961747?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/kszucs",
- "html_url": "https://github.com/kszucs",
- "followers_url": "https://api.github.com/users/kszucs/followers",
- "following_url": "https://api.github.com/users/kszucs/following{/other_user}",
- "gists_url": "https://api.github.com/users/kszucs/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/kszucs/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/kszucs/subscriptions",
- "organizations_url": "https://api.github.com/users/kszucs/orgs",
- "repos_url": "https://api.github.com/users/kszucs/repos",
- "events_url": "https://api.github.com/users/kszucs/events{/privacy}",
- "received_events_url": "https://api.github.com/users/kszucs/received_events",
- "type": "User",
- "site_admin": false
- }
-}
\ No newline at end of file
diff --git a/dev/archery/archery/tests/fixtures/issue-comment-480243811.json b/dev/archery/archery/tests/fixtures/issue-comment-480243811.json
deleted file mode 100644
index 93ee4b1..0000000
--- a/dev/archery/archery/tests/fixtures/issue-comment-480243811.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
- "url": "https://api.github.com/repos/ursa-labs/ursabot/issues/comments/479081273",
- "html_url": "https://github.com/ursa-labs/ursabot/pull/21#issuecomment-479081273",
- "issue_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/21",
- "id": 480243811,
- "node_id": "MDEyOklzc3VlQ29tbWVudDQ3OTA4MTI3Mw==",
- "user": {
- "login": "kszucs",
- "id": 961747,
- "node_id": "MDQ6VXNlcjk2MTc0Nw==",
- "avatar_url": "https://avatars1.githubusercontent.com/u/961747?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/kszucs",
- "html_url": "https://github.com/kszucs",
- "followers_url": "https://api.github.com/users/kszucs/followers",
- "following_url": "https://api.github.com/users/kszucs/following{/other_user}",
- "gists_url": "https://api.github.com/users/kszucs/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/kszucs/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/kszucs/subscriptions",
- "organizations_url": "https://api.github.com/users/kszucs/orgs",
- "repos_url": "https://api.github.com/users/kszucs/repos",
- "events_url": "https://api.github.com/users/kszucs/events{/privacy}",
- "received_events_url": "https://api.github.com/users/kszucs/received_events",
- "type": "User",
- "site_admin": false
- },
- "created_at": "2019-04-02T16:29:46Z",
- "updated_at": "2019-04-02T16:29:46Z",
- "author_association": "MEMBER",
- "body": "@ursabot"
-}
\ No newline at end of file
diff --git a/dev/archery/archery/tests/fixtures/issue-comment-480248726.json b/dev/archery/archery/tests/fixtures/issue-comment-480248726.json
deleted file mode 100644
index f3cd340..0000000
--- a/dev/archery/archery/tests/fixtures/issue-comment-480248726.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
- "url": "https://api.github.com/repos/ursa-labs/ursabot/issues/comments/480248726",
- "html_url": "https://github.com/ursa-labs/ursabot/pull/26#issuecomment-480248726",
- "issue_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/26",
- "id": 480248726,
- "node_id": "MDEyOklzc3VlQ29tbWVudDQ4MDI0ODcyNg==",
- "user": {
- "login": "kszucs",
- "id": 961747,
- "node_id": "MDQ6VXNlcjk2MTc0Nw==",
- "avatar_url": "https://avatars1.githubusercontent.com/u/961747?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/kszucs",
- "html_url": "https://github.com/kszucs",
- "followers_url": "https://api.github.com/users/kszucs/followers",
- "following_url": "https://api.github.com/users/kszucs/following{/other_user}",
- "gists_url": "https://api.github.com/users/kszucs/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/kszucs/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/kszucs/subscriptions",
- "organizations_url": "https://api.github.com/users/kszucs/orgs",
- "repos_url": "https://api.github.com/users/kszucs/repos",
- "events_url": "https://api.github.com/users/kszucs/events{/privacy}",
- "received_events_url": "https://api.github.com/users/kszucs/received_events",
- "type": "User",
- "site_admin": false
- },
- "created_at": "2019-04-05T11:55:43Z",
- "updated_at": "2019-04-05T11:55:43Z",
- "author_association": "MEMBER",
- "body": "@ursabot build"
-}
\ No newline at end of file
diff --git a/dev/archery/archery/tests/fixtures/pull-request-26-commit.json b/dev/archery/archery/tests/fixtures/pull-request-26-commit.json
deleted file mode 100644
index ffc4894..0000000
--- a/dev/archery/archery/tests/fixtures/pull-request-26-commit.json
+++ /dev/null
@@ -1,158 +0,0 @@
-{
- "sha": "2705da2b616b98fa6010a25813c5a7a27456f71d",
- "node_id": "MDY6Q29tbWl0MTY5MTAxNzAxOjI3MDVkYTJiNjE2Yjk4ZmE2MDEwYTI1ODEzYzVhN2EyNzQ1NmY3MWQ=",
- "commit": {
- "author": {
- "name": "Krisztián Szűcs",
- "email": "szucs.krisztian@gmail.com",
- "date": "2019-04-05T12:01:31Z"
- },
- "committer": {
- "name": "Krisztián Szűcs",
- "email": "szucs.krisztian@gmail.com",
- "date": "2019-04-05T12:01:31Z"
- },
- "message": "add recorded event requests",
- "tree": {
- "sha": "16a7bb186833a67e9c2d84a58393503b85500ceb",
- "url": "https://api.github.com/repos/ursa-labs/ursabot/git/trees/16a7bb186833a67e9c2d84a58393503b85500ceb"
- },
- "url": "https://api.github.com/repos/ursa-labs/ursabot/git/commits/2705da2b616b98fa6010a25813c5a7a27456f71d",
- "comment_count": 0,
- "verification": {
- "verified": true,
- "reason": "valid",
- "signature": "-----BEGIN PGP SIGNATURE-----\n\niQFOBAABCAA4FiEEOOW2r8dr6sA77zHlgjqBKYe1QKUFAlynQ58aHHN6dWNzLmty\naXN6dGlhbkBnbWFpbC5jb20ACgkQgjqBKYe1QKUYKwf6AiXDMaLqNLNSjRY7lIXX\nudioewz0hSb4bgIXBv30nswu9CoOA0+mHCokEVtZhYbXzXDsZ1KJrilSC4j+Ws4q\nkRGA6iEmrne2HcSKNZXzcVnwV9zpwKxlVh2QCTNb1PuOYFBLH0kwE704uWIWMGDN\nbo8cjQPwegePCRguCvPh/5wa5J3uiq5gmJLG6bC/d1XYE+FJVtlnyzqzLMIryGKe\ntIciw+wwkF413Q/YVbZ49vLUeCX9H8PHC4mZYGDWuvjFW1WTfkjK5bAH+oaTVM6h\n350I5ZFloHmMA/QeRge5qFxXoEBMDGiXHHktzYZDXnliFOQNxzqwirA5lQQ6LRSS\naQ==\n=7rqi\n-----END PGP SIGNATURE-----",
- "payload": "tree 16a7bb186833a67e9c2d84a58393503b85500ceb\nparent 446ae69b9385e8d0f40aa9595f723d34383af2f7\nauthor Krisztián Szűcs <szucs.krisztian@gmail.com> 1554465691 +0200\ncommitter Krisztián Szűcs <szucs.krisztian@gmail.com> 1554465691 +0200\n\nadd recorded event requests\n"
- }
- },
- "url": "https://api.github.com/repos/ursa-labs/ursabot/commits/2705da2b616b98fa6010a25813c5a7a27456f71d",
- "html_url": "https://github.com/ursa-labs/ursabot/commit/2705da2b616b98fa6010a25813c5a7a27456f71d",
- "comments_url": "https://api.github.com/repos/ursa-labs/ursabot/commits/2705da2b616b98fa6010a25813c5a7a27456f71d/comments",
- "author": {
- "login": "kszucs",
- "id": 961747,
- "node_id": "MDQ6VXNlcjk2MTc0Nw==",
- "avatar_url": "https://avatars1.githubusercontent.com/u/961747?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/kszucs",
- "html_url": "https://github.com/kszucs",
- "followers_url": "https://api.github.com/users/kszucs/followers",
- "following_url": "https://api.github.com/users/kszucs/following{/other_user}",
- "gists_url": "https://api.github.com/users/kszucs/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/kszucs/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/kszucs/subscriptions",
- "organizations_url": "https://api.github.com/users/kszucs/orgs",
- "repos_url": "https://api.github.com/users/kszucs/repos",
- "events_url": "https://api.github.com/users/kszucs/events{/privacy}",
- "received_events_url": "https://api.github.com/users/kszucs/received_events",
- "type": "User",
- "site_admin": false
- },
- "committer": {
- "login": "kszucs",
- "id": 961747,
- "node_id": "MDQ6VXNlcjk2MTc0Nw==",
- "avatar_url": "https://avatars1.githubusercontent.com/u/961747?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/kszucs",
- "html_url": "https://github.com/kszucs",
- "followers_url": "https://api.github.com/users/kszucs/followers",
- "following_url": "https://api.github.com/users/kszucs/following{/other_user}",
- "gists_url": "https://api.github.com/users/kszucs/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/kszucs/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/kszucs/subscriptions",
- "organizations_url": "https://api.github.com/users/kszucs/orgs",
- "repos_url": "https://api.github.com/users/kszucs/repos",
- "events_url": "https://api.github.com/users/kszucs/events{/privacy}",
- "received_events_url": "https://api.github.com/users/kszucs/received_events",
- "type": "User",
- "site_admin": false
- },
- "parents": [
- {
- "sha": "446ae69b9385e8d0f40aa9595f723d34383af2f7",
- "url": "https://api.github.com/repos/ursa-labs/ursabot/commits/446ae69b9385e8d0f40aa9595f723d34383af2f7",
- "html_url": "https://github.com/ursa-labs/ursabot/commit/446ae69b9385e8d0f40aa9595f723d34383af2f7"
- }
- ],
- "stats": {
- "total": 1062,
- "additions": 1058,
- "deletions": 4
- },
- "files": [
- {
- "sha": "dfae6eeaef384ae6180c6302a58b49e39982dc33",
- "filename": "ursabot/tests/fixtures/issue-comment-build-command.json",
- "status": "added",
- "additions": 212,
- "deletions": 0,
- "changes": 212,
- "blob_url": "https://github.com/ursa-labs/ursabot/blob/2705da2b616b98fa6010a25813c5a7a27456f71d/ursabot/tests/fixtures/issue-comment-build-command.json",
- "raw_url": "https://github.com/ursa-labs/ursabot/raw/2705da2b616b98fa6010a25813c5a7a27456f71d/ursabot/tests/fixtures/issue-comment-build-command.json",
- "contents_url": "https://api.github.com/repos/ursa-labs/ursabot/contents/ursabot/tests/fixtures/issue-comment-build-command.json?ref=2705da2b616b98fa6010a25813c5a7a27456f71d",
- "patch": "@@ -0,0 +1,212 @@\n+{\n+ \"action\": \"created\",\n+ \"comment\": {\n+ \"author_association\": \"NONE\",\n+ \"body\": \"I've successfully started builds for this PR\",\n+ \"created_at\": \"2019-04-05T11:55:44Z\",\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot/pull/26#issuecomment-480248730\",\n+ \"id\": 480248730,\n+ \"issue_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26\",\n+ \"node_id\": \"MDEyOklzc3VlQ29tbWVudDQ4MDI0ODczMA==\",\n+ \"updated_at\": \"2019-04-05T11:55:44Z\",\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/comments/480248730\",\n+ \"user\": {\n+ \"avatar_url\": \"https://avatars2.githubusercontent.com/u/49275095?v=4\",\n+ \"events_url\": \"https://api.github.com/users/ursabot/events{/privacy}\",\n+ \"followers_url\": \"https://api.github.com/users/ursabot/followers\",\n+ \"following_url\": \"https://api.github.com/users/ursabot/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/ursabot/gists{/gist_id}\",\n+ \"gravatar_id\": \"\",\n+ \"html_url\": \"https://github.com/ursabot\",\n+ \"id\": 49275095,\n+ \"login\": \"ursabot\",\n+ \"node_id\": \"MDQ6VXNlcjQ5Mjc1MDk1\",\n+ \"organizations_url\": \"https://api.github.com/users/ursabot/orgs\",\n+ \"received_events_url\": \"https://api.github.com/users/ursabot/received_events\",\n+ \"repos_url\": \"https://api.github.com/users/ursabot/repos\",\n+ \"site_admin\": false,\n+ \"starred_url\": \"https://api.github.com/users/ursabot/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/ursabot/subscriptions\",\n+ \"type\": \"User\",\n+ \"url\": \"https://api.github.com/users/ursabot\"\n+ }\n+ },\n+ \"issue\": {\n+ \"assignee\": null,\n+ \"assignees\": [],\n+ \"author_association\": \"MEMBER\",\n+ \"body\": \"\",\n+ \"closed_at\": null,\n+ \"comments\": 4,\n+ \"comments_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26/comments\",\n+ \"created_at\": \"2019-04-05T11:22:15Z\",\n+ \"events_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26/events\",\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot/pull/26\",\n+ \"id\": 429706959,\n+ \"labels\": [],\n+ \"labels_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26/labels{/name}\",\n+ \"locked\": false,\n+ \"milestone\": null,\n+ \"node_id\": \"MDExOlB1bGxSZXF1ZXN0MjY3Nzg1NTUy\",\n+ \"number\": 26,\n+ \"pull_request\": {\n+ \"diff_url\": \"https://github.com/ursa-labs/ursabot/pull/26.diff\",\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot/pull/26\",\n+ \"patch_url\": \"https://github.com/ursa-labs/ursabot/pull/26.patch\",\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot/pulls/26\"\n+ },\n+ \"repository_url\": \"https://api.github.com/repos/ursa-labs/ursabot\",\n+ \"state\": \"open\",\n+ \"title\": \"Unittests for GithubHook\",\n+ \"updated_at\": \"2019-04-05T11:55:44Z\",\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26\",\n+ \"user\": {\n+ \"avatar_url\": \"https://avatars1.githubusercontent.com/u/961747?v=4\",\n+ \"events_url\": \"https://api.github.com/users/kszucs/events{/privacy}\",\n+ \"followers_url\": \"https://api.github.com/users/kszucs/followers\",\n+ \"following_url\": \"https://api.github.com/users/kszucs/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/kszucs/gists{/gist_id}\",\n+ \"gravatar_id\": \"\",\n+ \"html_url\": \"https://github.com/kszucs\",\n+ \"id\": 961747,\n+ \"login\": \"kszucs\",\n+ \"node_id\": \"MDQ6VXNlcjk2MTc0Nw==\",\n+ \"organizations_url\": \"https://api.github.com/users/kszucs/orgs\",\n+ \"received_events_url\": \"https://api.github.com/users/kszucs/received_events\",\n+ \"repos_url\": \"https://api.github.com/users/kszucs/repos\",\n+ \"site_admin\": false,\n+ \"starred_url\": \"https://api.github.com/users/kszucs/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/kszucs/subscriptions\",\n+ \"type\": \"User\",\n+ \"url\": \"https://api.github.com/users/kszucs\"\n+ }\n+ },\n+ \"organization\": {\n+ \"avatar_url\": \"https://avatars2.githubusercontent.com/u/46514972?v=4\",\n+ \"description\": \"Innovation lab for open source data science tools, powered by Apache Arrow\",\n+ \"events_url\": \"https://api.github.com/orgs/ursa-labs/events\",\n+ \"hooks_url\": \"https://api.github.com/orgs/ursa-labs/hooks\",\n+ \"id\": 46514972,\n+ \"issues_url\": \"https://api.github.com/orgs/ursa-labs/issues\",\n+ \"login\": \"ursa-labs\",\n+ \"members_url\": \"https://api.github.com/orgs/ursa-labs/members{/member}\",\n+ \"node_id\": \"MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy\",\n+ \"public_members_url\": \"https://api.github.com/orgs/ursa-labs/public_members{/member}\",\n+ \"repos_url\": \"https://api.github.com/orgs/ursa-labs/repos\",\n+ \"url\": \"https://api.github.com/orgs/ursa-labs\"\n+ },\n+ \"repository\": {\n+ \"archive_url\": \"https://api.github.com/repos/ursa-labs/ursabot/{archive_format}{/ref}\",\n+ \"archived\": false,\n+ \"assignees_url\": \"https://api.github.com/repos/ursa-labs/ursabot/assignees{/user}\",\n+ \"blobs_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/blobs{/sha}\",\n+ \"branches_url\": \"https://api.github.com/repos/ursa-labs/ursabot/branches{/branch}\",\n+ \"clone_url\": \"https://github.com/ursa-labs/ursabot.git\",\n+ \"collaborators_url\": \"https://api.github.com/repos/ursa-labs/ursabot/collaborators{/collaborator}\",\n+ \"comments_url\": \"https://api.github.com/repos/ursa-labs/ursabot/comments{/number}\",\n+ \"commits_url\": \"https://api.github.com/repos/ursa-labs/ursabot/commits{/sha}\",\n+ \"compare_url\": \"https://api.github.com/repos/ursa-labs/ursabot/compare/{base}...{head}\",\n+ \"contents_url\": \"https://api.github.com/repos/ursa-labs/ursabot/contents/{+path}\",\n+ \"contributors_url\": \"https://api.github.com/repos/ursa-labs/ursabot/contributors\",\n+ \"created_at\": \"2019-02-04T15:40:31Z\",\n+ \"default_branch\": \"master\",\n+ \"deployments_url\": \"https://api.github.com/repos/ursa-labs/ursabot/deployments\",\n+ \"description\": null,\n+ \"disabled\": false,\n+ \"downloads_url\": \"https://api.github.com/repos/ursa-labs/ursabot/downloads\",\n+ \"events_url\": \"https://api.github.com/repos/ursa-labs/ursabot/events\",\n+ \"fork\": false,\n+ \"forks\": 0,\n+ \"forks_count\": 0,\n+ \"forks_url\": \"https://api.github.com/repos/ursa-labs/ursabot/forks\",\n+ \"full_name\": \"ursa-labs/ursabot\",\n+ \"git_commits_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/commits{/sha}\",\n+ \"git_refs_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/refs{/sha}\",\n+ \"git_tags_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/tags{/sha}\",\n+ \"git_url\": \"git://github.com/ursa-labs/ursabot.git\",\n+ \"has_downloads\": true,\n+ \"has_issues\": true,\n+ \"has_pages\": false,\n+ \"has_projects\": true,\n+ \"has_wiki\": true,\n+ \"homepage\": null,\n+ \"hooks_url\": \"https://api.github.com/repos/ursa-labs/ursabot/hooks\",\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot\",\n+ \"id\": 169101701,\n+ \"issue_comment_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/comments{/number}\",\n+ \"issue_events_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/events{/number}\",\n+ \"issues_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues{/number}\",\n+ \"keys_url\": \"https://api.github.com/repos/ursa-labs/ursabot/keys{/key_id}\",\n+ \"labels_url\": \"https://api.github.com/repos/ursa-labs/ursabot/labels{/name}\",\n+ \"language\": \"Jupyter Notebook\",\n+ \"languages_url\": \"https://api.github.com/repos/ursa-labs/ursabot/languages\",\n+ \"license\": null,\n+ \"merges_url\": \"https://api.github.com/repos/ursa-labs/ursabot/merges\",\n+ \"milestones_url\": \"https://api.github.com/repos/ursa-labs/ursabot/milestones{/number}\",\n+ \"mirror_url\": null,\n+ \"name\": \"ursabot\",\n+ \"node_id\": \"MDEwOlJlcG9zaXRvcnkxNjkxMDE3MDE=\",\n+ \"notifications_url\": \"https://api.github.com/repos/ursa-labs/ursabot/notifications{?since,all,participating}\",\n+ \"open_issues\": 19,\n+ \"open_issues_count\": 19,\n+ \"owner\": {\n+ \"avatar_url\": \"https://avatars2.githubusercontent.com/u/46514972?v=4\",\n+ \"events_url\": \"https://api.github.com/users/ursa-labs/events{/privacy}\",\n+ \"followers_url\": \"https://api.github.com/users/ursa-labs/followers\",\n+ \"following_url\": \"https://api.github.com/users/ursa-labs/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/ursa-labs/gists{/gist_id}\",\n+ \"gravatar_id\": \"\",\n+ \"html_url\": \"https://github.com/ursa-labs\",\n+ \"id\": 46514972,\n+ \"login\": \"ursa-labs\",\n+ \"node_id\": \"MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy\",\n+ \"organizations_url\": \"https://api.github.com/users/ursa-labs/orgs\",\n+ \"received_events_url\": \"https://api.github.com/users/ursa-labs/received_events\",\n+ \"repos_url\": \"https://api.github.com/users/ursa-labs/repos\",\n+ \"site_admin\": false,\n+ \"starred_url\": \"https://api.github.com/users/ursa-labs/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/ursa-labs/subscriptions\",\n+ \"type\": \"Organization\",\n+ \"url\": \"https://api.github.com/users/ursa-labs\"\n+ },\n+ \"private\": false,\n+ \"pulls_url\": \"https://api.github.com/repos/ursa-labs/ursabot/pulls{/number}\",\n+ \"pushed_at\": \"2019-04-05T11:22:16Z\",\n+ \"releases_url\": \"https://api.github.com/repos/ursa-labs/ursabot/releases{/id}\",\n+ \"size\": 892,\n+ \"ssh_url\": \"git@github.com:ursa-labs/ursabot.git\",\n+ \"stargazers_count\": 1,\n+ \"stargazers_url\": \"https://api.github.com/repos/ursa-labs/ursabot/stargazers\",\n+ \"statuses_url\": \"https://api.github.com/repos/ursa-labs/ursabot/statuses/{sha}\",\n+ \"subscribers_url\": \"https://api.github.com/repos/ursa-labs/ursabot/subscribers\",\n+ \"subscription_url\": \"https://api.github.com/repos/ursa-labs/ursabot/subscription\",\n+ \"svn_url\": \"https://github.com/ursa-labs/ursabot\",\n+ \"tags_url\": \"https://api.github.com/repos/ursa-labs/ursabot/tags\",\n+ \"teams_url\": \"https://api.github.com/repos/ursa-labs/ursabot/teams\",\n+ \"trees_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/trees{/sha}\",\n+ \"updated_at\": \"2019-04-04T17:49:10Z\",\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot\",\n+ \"watchers\": 1,\n+ \"watchers_count\": 1\n+ },\n+ \"sender\": {\n+ \"avatar_url\": \"https://avatars2.githubusercontent.com/u/49275095?v=4\",\n+ \"events_url\": \"https://api.github.com/users/ursabot/events{/privacy}\",\n+ \"followers_url\": \"https://api.github.com/users/ursabot/followers\",\n+ \"following_url\": \"https://api.github.com/users/ursabot/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/ursabot/gists{/gist_id}\",\n+ \"gravatar_id\": \"\",\n+ \"html_url\": \"https://github.com/ursabot\",\n+ \"id\": 49275095,\n+ \"login\": \"ursabot\",\n+ \"node_id\": \"MDQ6VXNlcjQ5Mjc1MDk1\",\n+ \"organizations_url\": \"https://api.github.com/users/ursabot/orgs\",\n+ \"received_events_url\": \"https://api.github.com/users/ursabot/received_events\",\n+ \"repos_url\": \"https://api.github.com/users/ursabot/repos\",\n+ \"site_admin\": false,\n+ \"starred_url\": \"https://api.github.com/users/ursabot/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/ursabot/subscriptions\",\n+ \"type\": \"User\",\n+ \"url\": \"https://api.github.com/users/ursabot\"\n+ }\n+}"
- },
- {
- "sha": "7ef554e333327f0e62aa1fd76b4b17844a39adeb",
- "filename": "ursabot/tests/fixtures/issue-comment-by-ursabot.json",
- "status": "added",
- "additions": 212,
- "deletions": 0,
- "changes": 212,
- "blob_url": "https://github.com/ursa-labs/ursabot/blob/2705da2b616b98fa6010a25813c5a7a27456f71d/ursabot/tests/fixtures/issue-comment-by-ursabot.json",
- "raw_url": "https://github.com/ursa-labs/ursabot/raw/2705da2b616b98fa6010a25813c5a7a27456f71d/ursabot/tests/fixtures/issue-comment-by-ursabot.json",
- "contents_url": "https://api.github.com/repos/ursa-labs/ursabot/contents/ursabot/tests/fixtures/issue-comment-by-ursabot.json?ref=2705da2b616b98fa6010a25813c5a7a27456f71d",
- "patch": "@@ -0,0 +1,212 @@\n+{\n+ \"action\": \"created\",\n+ \"comment\": {\n+ \"author_association\": \"NONE\",\n+ \"body\": \"Unknown command \\\"\\\"\",\n+ \"created_at\": \"2019-04-05T11:35:47Z\",\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot/pull/26#issuecomment-480243815\",\n+ \"id\": 480243815,\n+ \"issue_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26\",\n+ \"node_id\": \"MDEyOklzc3VlQ29tbWVudDQ4MDI0MzgxNQ==\",\n+ \"updated_at\": \"2019-04-05T11:35:47Z\",\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/comments/480243815\",\n+ \"user\": {\n+ \"avatar_url\": \"https://avatars2.githubusercontent.com/u/49275095?v=4\",\n+ \"events_url\": \"https://api.github.com/users/ursabot/events{/privacy}\",\n+ \"followers_url\": \"https://api.github.com/users/ursabot/followers\",\n+ \"following_url\": \"https://api.github.com/users/ursabot/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/ursabot/gists{/gist_id}\",\n+ \"gravatar_id\": \"\",\n+ \"html_url\": \"https://github.com/ursabot\",\n+ \"id\": 49275095,\n+ \"login\": \"ursabot\",\n+ \"node_id\": \"MDQ6VXNlcjQ5Mjc1MDk1\",\n+ \"organizations_url\": \"https://api.github.com/users/ursabot/orgs\",\n+ \"received_events_url\": \"https://api.github.com/users/ursabot/received_events\",\n+ \"repos_url\": \"https://api.github.com/users/ursabot/repos\",\n+ \"site_admin\": false,\n+ \"starred_url\": \"https://api.github.com/users/ursabot/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/ursabot/subscriptions\",\n+ \"type\": \"User\",\n+ \"url\": \"https://api.github.com/users/ursabot\"\n+ }\n+ },\n+ \"issue\": {\n+ \"assignee\": null,\n+ \"assignees\": [],\n+ \"author_association\": \"MEMBER\",\n+ \"body\": \"\",\n+ \"closed_at\": null,\n+ \"comments\": 2,\n+ \"comments_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26/comments\",\n+ \"created_at\": \"2019-04-05T11:22:15Z\",\n+ \"events_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26/events\",\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot/pull/26\",\n+ \"id\": 429706959,\n+ \"labels\": [],\n+ \"labels_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26/labels{/name}\",\n+ \"locked\": false,\n+ \"milestone\": null,\n+ \"node_id\": \"MDExOlB1bGxSZXF1ZXN0MjY3Nzg1NTUy\",\n+ \"number\": 26,\n+ \"pull_request\": {\n+ \"diff_url\": \"https://github.com/ursa-labs/ursabot/pull/26.diff\",\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot/pull/26\",\n+ \"patch_url\": \"https://github.com/ursa-labs/ursabot/pull/26.patch\",\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot/pulls/26\"\n+ },\n+ \"repository_url\": \"https://api.github.com/repos/ursa-labs/ursabot\",\n+ \"state\": \"open\",\n+ \"title\": \"Unittests for GithubHook\",\n+ \"updated_at\": \"2019-04-05T11:35:47Z\",\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26\",\n+ \"user\": {\n+ \"avatar_url\": \"https://avatars1.githubusercontent.com/u/961747?v=4\",\n+ \"events_url\": \"https://api.github.com/users/kszucs/events{/privacy}\",\n+ \"followers_url\": \"https://api.github.com/users/kszucs/followers\",\n+ \"following_url\": \"https://api.github.com/users/kszucs/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/kszucs/gists{/gist_id}\",\n+ \"gravatar_id\": \"\",\n+ \"html_url\": \"https://github.com/kszucs\",\n+ \"id\": 961747,\n+ \"login\": \"kszucs\",\n+ \"node_id\": \"MDQ6VXNlcjk2MTc0Nw==\",\n+ \"organizations_url\": \"https://api.github.com/users/kszucs/orgs\",\n+ \"received_events_url\": \"https://api.github.com/users/kszucs/received_events\",\n+ \"repos_url\": \"https://api.github.com/users/kszucs/repos\",\n+ \"site_admin\": false,\n+ \"starred_url\": \"https://api.github.com/users/kszucs/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/kszucs/subscriptions\",\n+ \"type\": \"User\",\n+ \"url\": \"https://api.github.com/users/kszucs\"\n+ }\n+ },\n+ \"organization\": {\n+ \"avatar_url\": \"https://avatars2.githubusercontent.com/u/46514972?v=4\",\n+ \"description\": \"Innovation lab for open source data science tools, powered by Apache Arrow\",\n+ \"events_url\": \"https://api.github.com/orgs/ursa-labs/events\",\n+ \"hooks_url\": \"https://api.github.com/orgs/ursa-labs/hooks\",\n+ \"id\": 46514972,\n+ \"issues_url\": \"https://api.github.com/orgs/ursa-labs/issues\",\n+ \"login\": \"ursa-labs\",\n+ \"members_url\": \"https://api.github.com/orgs/ursa-labs/members{/member}\",\n+ \"node_id\": \"MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy\",\n+ \"public_members_url\": \"https://api.github.com/orgs/ursa-labs/public_members{/member}\",\n+ \"repos_url\": \"https://api.github.com/orgs/ursa-labs/repos\",\n+ \"url\": \"https://api.github.com/orgs/ursa-labs\"\n+ },\n+ \"repository\": {\n+ \"archive_url\": \"https://api.github.com/repos/ursa-labs/ursabot/{archive_format}{/ref}\",\n+ \"archived\": false,\n+ \"assignees_url\": \"https://api.github.com/repos/ursa-labs/ursabot/assignees{/user}\",\n+ \"blobs_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/blobs{/sha}\",\n+ \"branches_url\": \"https://api.github.com/repos/ursa-labs/ursabot/branches{/branch}\",\n+ \"clone_url\": \"https://github.com/ursa-labs/ursabot.git\",\n+ \"collaborators_url\": \"https://api.github.com/repos/ursa-labs/ursabot/collaborators{/collaborator}\",\n+ \"comments_url\": \"https://api.github.com/repos/ursa-labs/ursabot/comments{/number}\",\n+ \"commits_url\": \"https://api.github.com/repos/ursa-labs/ursabot/commits{/sha}\",\n+ \"compare_url\": \"https://api.github.com/repos/ursa-labs/ursabot/compare/{base}...{head}\",\n+ \"contents_url\": \"https://api.github.com/repos/ursa-labs/ursabot/contents/{+path}\",\n+ \"contributors_url\": \"https://api.github.com/repos/ursa-labs/ursabot/contributors\",\n+ \"created_at\": \"2019-02-04T15:40:31Z\",\n+ \"default_branch\": \"master\",\n+ \"deployments_url\": \"https://api.github.com/repos/ursa-labs/ursabot/deployments\",\n+ \"description\": null,\n+ \"disabled\": false,\n+ \"downloads_url\": \"https://api.github.com/repos/ursa-labs/ursabot/downloads\",\n+ \"events_url\": \"https://api.github.com/repos/ursa-labs/ursabot/events\",\n+ \"fork\": false,\n+ \"forks\": 0,\n+ \"forks_count\": 0,\n+ \"forks_url\": \"https://api.github.com/repos/ursa-labs/ursabot/forks\",\n+ \"full_name\": \"ursa-labs/ursabot\",\n+ \"git_commits_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/commits{/sha}\",\n+ \"git_refs_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/refs{/sha}\",\n+ \"git_tags_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/tags{/sha}\",\n+ \"git_url\": \"git://github.com/ursa-labs/ursabot.git\",\n+ \"has_downloads\": true,\n+ \"has_issues\": true,\n+ \"has_pages\": false,\n+ \"has_projects\": true,\n+ \"has_wiki\": true,\n+ \"homepage\": null,\n+ \"hooks_url\": \"https://api.github.com/repos/ursa-labs/ursabot/hooks\",\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot\",\n+ \"id\": 169101701,\n+ \"issue_comment_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/comments{/number}\",\n+ \"issue_events_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/events{/number}\",\n+ \"issues_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues{/number}\",\n+ \"keys_url\": \"https://api.github.com/repos/ursa-labs/ursabot/keys{/key_id}\",\n+ \"labels_url\": \"https://api.github.com/repos/ursa-labs/ursabot/labels{/name}\",\n+ \"language\": \"Jupyter Notebook\",\n+ \"languages_url\": \"https://api.github.com/repos/ursa-labs/ursabot/languages\",\n+ \"license\": null,\n+ \"merges_url\": \"https://api.github.com/repos/ursa-labs/ursabot/merges\",\n+ \"milestones_url\": \"https://api.github.com/repos/ursa-labs/ursabot/milestones{/number}\",\n+ \"mirror_url\": null,\n+ \"name\": \"ursabot\",\n+ \"node_id\": \"MDEwOlJlcG9zaXRvcnkxNjkxMDE3MDE=\",\n+ \"notifications_url\": \"https://api.github.com/repos/ursa-labs/ursabot/notifications{?since,all,participating}\",\n+ \"open_issues\": 19,\n+ \"open_issues_count\": 19,\n+ \"owner\": {\n+ \"avatar_url\": \"https://avatars2.githubusercontent.com/u/46514972?v=4\",\n+ \"events_url\": \"https://api.github.com/users/ursa-labs/events{/privacy}\",\n+ \"followers_url\": \"https://api.github.com/users/ursa-labs/followers\",\n+ \"following_url\": \"https://api.github.com/users/ursa-labs/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/ursa-labs/gists{/gist_id}\",\n+ \"gravatar_id\": \"\",\n+ \"html_url\": \"https://github.com/ursa-labs\",\n+ \"id\": 46514972,\n+ \"login\": \"ursa-labs\",\n+ \"node_id\": \"MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy\",\n+ \"organizations_url\": \"https://api.github.com/users/ursa-labs/orgs\",\n+ \"received_events_url\": \"https://api.github.com/users/ursa-labs/received_events\",\n+ \"repos_url\": \"https://api.github.com/users/ursa-labs/repos\",\n+ \"site_admin\": false,\n+ \"starred_url\": \"https://api.github.com/users/ursa-labs/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/ursa-labs/subscriptions\",\n+ \"type\": \"Organization\",\n+ \"url\": \"https://api.github.com/users/ursa-labs\"\n+ },\n+ \"private\": false,\n+ \"pulls_url\": \"https://api.github.com/repos/ursa-labs/ursabot/pulls{/number}\",\n+ \"pushed_at\": \"2019-04-05T11:22:16Z\",\n+ \"releases_url\": \"https://api.github.com/repos/ursa-labs/ursabot/releases{/id}\",\n+ \"size\": 892,\n+ \"ssh_url\": \"git@github.com:ursa-labs/ursabot.git\",\n+ \"stargazers_count\": 1,\n+ \"stargazers_url\": \"https://api.github.com/repos/ursa-labs/ursabot/stargazers\",\n+ \"statuses_url\": \"https://api.github.com/repos/ursa-labs/ursabot/statuses/{sha}\",\n+ \"subscribers_url\": \"https://api.github.com/repos/ursa-labs/ursabot/subscribers\",\n+ \"subscription_url\": \"https://api.github.com/repos/ursa-labs/ursabot/subscription\",\n+ \"svn_url\": \"https://github.com/ursa-labs/ursabot\",\n+ \"tags_url\": \"https://api.github.com/repos/ursa-labs/ursabot/tags\",\n+ \"teams_url\": \"https://api.github.com/repos/ursa-labs/ursabot/teams\",\n+ \"trees_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/trees{/sha}\",\n+ \"updated_at\": \"2019-04-04T17:49:10Z\",\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot\",\n+ \"watchers\": 1,\n+ \"watchers_count\": 1\n+ },\n+ \"sender\": {\n+ \"avatar_url\": \"https://avatars2.githubusercontent.com/u/49275095?v=4\",\n+ \"events_url\": \"https://api.github.com/users/ursabot/events{/privacy}\",\n+ \"followers_url\": \"https://api.github.com/users/ursabot/followers\",\n+ \"following_url\": \"https://api.github.com/users/ursabot/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/ursabot/gists{/gist_id}\",\n+ \"gravatar_id\": \"\",\n+ \"html_url\": \"https://github.com/ursabot\",\n+ \"id\": 49275095,\n+ \"login\": \"ursabot\",\n+ \"node_id\": \"MDQ6VXNlcjQ5Mjc1MDk1\",\n+ \"organizations_url\": \"https://api.github.com/users/ursabot/orgs\",\n+ \"received_events_url\": \"https://api.github.com/users/ursabot/received_events\",\n+ \"repos_url\": \"https://api.github.com/users/ursabot/repos\",\n+ \"site_admin\": false,\n+ \"starred_url\": \"https://api.github.com/users/ursabot/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/ursabot/subscriptions\",\n+ \"type\": \"User\",\n+ \"url\": \"https://api.github.com/users/ursabot\"\n+ }\n+}"
- },
- {
- "sha": "a8082dbc91fdfe815b795e49ec10e49000771ef5",
- "filename": "ursabot/tests/fixtures/issue-comment-not-mentioning-ursabot.json",
- "status": "added",
- "additions": 212,
- "deletions": 0,
- "changes": 212,
- "blob_url": "https://github.com/ursa-labs/ursabot/blob/2705da2b616b98fa6010a25813c5a7a27456f71d/ursabot/tests/fixtures/issue-comment-not-mentioning-ursabot.json",
- "raw_url": "https://github.com/ursa-labs/ursabot/raw/2705da2b616b98fa6010a25813c5a7a27456f71d/ursabot/tests/fixtures/issue-comment-not-mentioning-ursabot.json",
- "contents_url": "https://api.github.com/repos/ursa-labs/ursabot/contents/ursabot/tests/fixtures/issue-comment-not-mentioning-ursabot.json?ref=2705da2b616b98fa6010a25813c5a7a27456f71d",
- "patch": "@@ -0,0 +1,212 @@\n+{\n+ \"action\": \"created\",\n+ \"comment\": {\n+ \"author_association\": \"MEMBER\",\n+ \"body\": \"bear is no game\",\n+ \"created_at\": \"2019-04-05T11:26:56Z\",\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot/pull/26#issuecomment-480241727\",\n+ \"id\": 480241727,\n+ \"issue_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26\",\n+ \"node_id\": \"MDEyOklzc3VlQ29tbWVudDQ4MDI0MTcyNw==\",\n+ \"updated_at\": \"2019-04-05T11:26:56Z\",\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/comments/480241727\",\n+ \"user\": {\n+ \"avatar_url\": \"https://avatars1.githubusercontent.com/u/961747?v=4\",\n+ \"events_url\": \"https://api.github.com/users/kszucs/events{/privacy}\",\n+ \"followers_url\": \"https://api.github.com/users/kszucs/followers\",\n+ \"following_url\": \"https://api.github.com/users/kszucs/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/kszucs/gists{/gist_id}\",\n+ \"gravatar_id\": \"\",\n+ \"html_url\": \"https://github.com/kszucs\",\n+ \"id\": 961747,\n+ \"login\": \"kszucs\",\n+ \"node_id\": \"MDQ6VXNlcjk2MTc0Nw==\",\n+ \"organizations_url\": \"https://api.github.com/users/kszucs/orgs\",\n+ \"received_events_url\": \"https://api.github.com/users/kszucs/received_events\",\n+ \"repos_url\": \"https://api.github.com/users/kszucs/repos\",\n+ \"site_admin\": false,\n+ \"starred_url\": \"https://api.github.com/users/kszucs/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/kszucs/subscriptions\",\n+ \"type\": \"User\",\n+ \"url\": \"https://api.github.com/users/kszucs\"\n+ }\n+ },\n+ \"issue\": {\n+ \"assignee\": null,\n+ \"assignees\": [],\n+ \"author_association\": \"MEMBER\",\n+ \"body\": \"\",\n+ \"closed_at\": null,\n+ \"comments\": 0,\n+ \"comments_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26/comments\",\n+ \"created_at\": \"2019-04-05T11:22:15Z\",\n+ \"events_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26/events\",\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot/pull/26\",\n+ \"id\": 429706959,\n+ \"labels\": [],\n+ \"labels_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26/labels{/name}\",\n+ \"locked\": false,\n+ \"milestone\": null,\n+ \"node_id\": \"MDExOlB1bGxSZXF1ZXN0MjY3Nzg1NTUy\",\n+ \"number\": 26,\n+ \"pull_request\": {\n+ \"diff_url\": \"https://github.com/ursa-labs/ursabot/pull/26.diff\",\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot/pull/26\",\n+ \"patch_url\": \"https://github.com/ursa-labs/ursabot/pull/26.patch\",\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot/pulls/26\"\n+ },\n+ \"repository_url\": \"https://api.github.com/repos/ursa-labs/ursabot\",\n+ \"state\": \"open\",\n+ \"title\": \"Unittests for GithubHook\",\n+ \"updated_at\": \"2019-04-05T11:26:56Z\",\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26\",\n+ \"user\": {\n+ \"avatar_url\": \"https://avatars1.githubusercontent.com/u/961747?v=4\",\n+ \"events_url\": \"https://api.github.com/users/kszucs/events{/privacy}\",\n+ \"followers_url\": \"https://api.github.com/users/kszucs/followers\",\n+ \"following_url\": \"https://api.github.com/users/kszucs/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/kszucs/gists{/gist_id}\",\n+ \"gravatar_id\": \"\",\n+ \"html_url\": \"https://github.com/kszucs\",\n+ \"id\": 961747,\n+ \"login\": \"kszucs\",\n+ \"node_id\": \"MDQ6VXNlcjk2MTc0Nw==\",\n+ \"organizations_url\": \"https://api.github.com/users/kszucs/orgs\",\n+ \"received_events_url\": \"https://api.github.com/users/kszucs/received_events\",\n+ \"repos_url\": \"https://api.github.com/users/kszucs/repos\",\n+ \"site_admin\": false,\n+ \"starred_url\": \"https://api.github.com/users/kszucs/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/kszucs/subscriptions\",\n+ \"type\": \"User\",\n+ \"url\": \"https://api.github.com/users/kszucs\"\n+ }\n+ },\n+ \"organization\": {\n+ \"avatar_url\": \"https://avatars2.githubusercontent.com/u/46514972?v=4\",\n+ \"description\": \"Innovation lab for open source data science tools, powered by Apache Arrow\",\n+ \"events_url\": \"https://api.github.com/orgs/ursa-labs/events\",\n+ \"hooks_url\": \"https://api.github.com/orgs/ursa-labs/hooks\",\n+ \"id\": 46514972,\n+ \"issues_url\": \"https://api.github.com/orgs/ursa-labs/issues\",\n+ \"login\": \"ursa-labs\",\n+ \"members_url\": \"https://api.github.com/orgs/ursa-labs/members{/member}\",\n+ \"node_id\": \"MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy\",\n+ \"public_members_url\": \"https://api.github.com/orgs/ursa-labs/public_members{/member}\",\n+ \"repos_url\": \"https://api.github.com/orgs/ursa-labs/repos\",\n+ \"url\": \"https://api.github.com/orgs/ursa-labs\"\n+ },\n+ \"repository\": {\n+ \"archive_url\": \"https://api.github.com/repos/ursa-labs/ursabot/{archive_format}{/ref}\",\n+ \"archived\": false,\n+ \"assignees_url\": \"https://api.github.com/repos/ursa-labs/ursabot/assignees{/user}\",\n+ \"blobs_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/blobs{/sha}\",\n+ \"branches_url\": \"https://api.github.com/repos/ursa-labs/ursabot/branches{/branch}\",\n+ \"clone_url\": \"https://github.com/ursa-labs/ursabot.git\",\n+ \"collaborators_url\": \"https://api.github.com/repos/ursa-labs/ursabot/collaborators{/collaborator}\",\n+ \"comments_url\": \"https://api.github.com/repos/ursa-labs/ursabot/comments{/number}\",\n+ \"commits_url\": \"https://api.github.com/repos/ursa-labs/ursabot/commits{/sha}\",\n+ \"compare_url\": \"https://api.github.com/repos/ursa-labs/ursabot/compare/{base}...{head}\",\n+ \"contents_url\": \"https://api.github.com/repos/ursa-labs/ursabot/contents/{+path}\",\n+ \"contributors_url\": \"https://api.github.com/repos/ursa-labs/ursabot/contributors\",\n+ \"created_at\": \"2019-02-04T15:40:31Z\",\n+ \"default_branch\": \"master\",\n+ \"deployments_url\": \"https://api.github.com/repos/ursa-labs/ursabot/deployments\",\n+ \"description\": null,\n+ \"disabled\": false,\n+ \"downloads_url\": \"https://api.github.com/repos/ursa-labs/ursabot/downloads\",\n+ \"events_url\": \"https://api.github.com/repos/ursa-labs/ursabot/events\",\n+ \"fork\": false,\n+ \"forks\": 0,\n+ \"forks_count\": 0,\n+ \"forks_url\": \"https://api.github.com/repos/ursa-labs/ursabot/forks\",\n+ \"full_name\": \"ursa-labs/ursabot\",\n+ \"git_commits_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/commits{/sha}\",\n+ \"git_refs_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/refs{/sha}\",\n+ \"git_tags_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/tags{/sha}\",\n+ \"git_url\": \"git://github.com/ursa-labs/ursabot.git\",\n+ \"has_downloads\": true,\n+ \"has_issues\": true,\n+ \"has_pages\": false,\n+ \"has_projects\": true,\n+ \"has_wiki\": true,\n+ \"homepage\": null,\n+ \"hooks_url\": \"https://api.github.com/repos/ursa-labs/ursabot/hooks\",\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot\",\n+ \"id\": 169101701,\n+ \"issue_comment_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/comments{/number}\",\n+ \"issue_events_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/events{/number}\",\n+ \"issues_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues{/number}\",\n+ \"keys_url\": \"https://api.github.com/repos/ursa-labs/ursabot/keys{/key_id}\",\n+ \"labels_url\": \"https://api.github.com/repos/ursa-labs/ursabot/labels{/name}\",\n+ \"language\": \"Jupyter Notebook\",\n+ \"languages_url\": \"https://api.github.com/repos/ursa-labs/ursabot/languages\",\n+ \"license\": null,\n+ \"merges_url\": \"https://api.github.com/repos/ursa-labs/ursabot/merges\",\n+ \"milestones_url\": \"https://api.github.com/repos/ursa-labs/ursabot/milestones{/number}\",\n+ \"mirror_url\": null,\n+ \"name\": \"ursabot\",\n+ \"node_id\": \"MDEwOlJlcG9zaXRvcnkxNjkxMDE3MDE=\",\n+ \"notifications_url\": \"https://api.github.com/repos/ursa-labs/ursabot/notifications{?since,all,participating}\",\n+ \"open_issues\": 19,\n+ \"open_issues_count\": 19,\n+ \"owner\": {\n+ \"avatar_url\": \"https://avatars2.githubusercontent.com/u/46514972?v=4\",\n+ \"events_url\": \"https://api.github.com/users/ursa-labs/events{/privacy}\",\n+ \"followers_url\": \"https://api.github.com/users/ursa-labs/followers\",\n+ \"following_url\": \"https://api.github.com/users/ursa-labs/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/ursa-labs/gists{/gist_id}\",\n+ \"gravatar_id\": \"\",\n+ \"html_url\": \"https://github.com/ursa-labs\",\n+ \"id\": 46514972,\n+ \"login\": \"ursa-labs\",\n+ \"node_id\": \"MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy\",\n+ \"organizations_url\": \"https://api.github.com/users/ursa-labs/orgs\",\n+ \"received_events_url\": \"https://api.github.com/users/ursa-labs/received_events\",\n+ \"repos_url\": \"https://api.github.com/users/ursa-labs/repos\",\n+ \"site_admin\": false,\n+ \"starred_url\": \"https://api.github.com/users/ursa-labs/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/ursa-labs/subscriptions\",\n+ \"type\": \"Organization\",\n+ \"url\": \"https://api.github.com/users/ursa-labs\"\n+ },\n+ \"private\": false,\n+ \"pulls_url\": \"https://api.github.com/repos/ursa-labs/ursabot/pulls{/number}\",\n+ \"pushed_at\": \"2019-04-05T11:22:16Z\",\n+ \"releases_url\": \"https://api.github.com/repos/ursa-labs/ursabot/releases{/id}\",\n+ \"size\": 892,\n+ \"ssh_url\": \"git@github.com:ursa-labs/ursabot.git\",\n+ \"stargazers_count\": 1,\n+ \"stargazers_url\": \"https://api.github.com/repos/ursa-labs/ursabot/stargazers\",\n+ \"statuses_url\": \"https://api.github.com/repos/ursa-labs/ursabot/statuses/{sha}\",\n+ \"subscribers_url\": \"https://api.github.com/repos/ursa-labs/ursabot/subscribers\",\n+ \"subscription_url\": \"https://api.github.com/repos/ursa-labs/ursabot/subscription\",\n+ \"svn_url\": \"https://github.com/ursa-labs/ursabot\",\n+ \"tags_url\": \"https://api.github.com/repos/ursa-labs/ursabot/tags\",\n+ \"teams_url\": \"https://api.github.com/repos/ursa-labs/ursabot/teams\",\n+ \"trees_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/trees{/sha}\",\n+ \"updated_at\": \"2019-04-04T17:49:10Z\",\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot\",\n+ \"watchers\": 1,\n+ \"watchers_count\": 1\n+ },\n+ \"sender\": {\n+ \"avatar_url\": \"https://avatars1.githubusercontent.com/u/961747?v=4\",\n+ \"events_url\": \"https://api.github.com/users/kszucs/events{/privacy}\",\n+ \"followers_url\": \"https://api.github.com/users/kszucs/followers\",\n+ \"following_url\": \"https://api.github.com/users/kszucs/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/kszucs/gists{/gist_id}\",\n+ \"gravatar_id\": \"\",\n+ \"html_url\": \"https://github.com/kszucs\",\n+ \"id\": 961747,\n+ \"login\": \"kszucs\",\n+ \"node_id\": \"MDQ6VXNlcjk2MTc0Nw==\",\n+ \"organizations_url\": \"https://api.github.com/users/kszucs/orgs\",\n+ \"received_events_url\": \"https://api.github.com/users/kszucs/received_events\",\n+ \"repos_url\": \"https://api.github.com/users/kszucs/repos\",\n+ \"site_admin\": false,\n+ \"starred_url\": \"https://api.github.com/users/kszucs/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/kszucs/subscriptions\",\n+ \"type\": \"User\",\n+ \"url\": \"https://api.github.com/users/kszucs\"\n+ }\n+}"
- },
- {
- "sha": "2770e29ba9086394455315e590c0b433d08e437e",
- "filename": "ursabot/tests/fixtures/issue-comment-with-empty-command.json",
- "status": "added",
- "additions": 212,
- "deletions": 0,
- "changes": 212,
- "blob_url": "https://github.com/ursa-labs/ursabot/blob/2705da2b616b98fa6010a25813c5a7a27456f71d/ursabot/tests/fixtures/issue-comment-with-empty-command.json",
- "raw_url": "https://github.com/ursa-labs/ursabot/raw/2705da2b616b98fa6010a25813c5a7a27456f71d/ursabot/tests/fixtures/issue-comment-with-empty-command.json",
- "contents_url": "https://api.github.com/repos/ursa-labs/ursabot/contents/ursabot/tests/fixtures/issue-comment-with-empty-command.json?ref=2705da2b616b98fa6010a25813c5a7a27456f71d",
- "patch": "@@ -0,0 +1,212 @@\n+{\n+ \"action\": \"created\",\n+ \"comment\": {\n+ \"author_association\": \"MEMBER\",\n+ \"body\": \"@ursabot \",\n+ \"created_at\": \"2019-04-05T11:35:46Z\",\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot/pull/26#issuecomment-480243811\",\n+ \"id\": 480243811,\n+ \"issue_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26\",\n+ \"node_id\": \"MDEyOklzc3VlQ29tbWVudDQ4MDI0MzgxMQ==\",\n+ \"updated_at\": \"2019-04-05T11:35:46Z\",\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/comments/480243811\",\n+ \"user\": {\n+ \"avatar_url\": \"https://avatars1.githubusercontent.com/u/961747?v=4\",\n+ \"events_url\": \"https://api.github.com/users/kszucs/events{/privacy}\",\n+ \"followers_url\": \"https://api.github.com/users/kszucs/followers\",\n+ \"following_url\": \"https://api.github.com/users/kszucs/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/kszucs/gists{/gist_id}\",\n+ \"gravatar_id\": \"\",\n+ \"html_url\": \"https://github.com/kszucs\",\n+ \"id\": 961747,\n+ \"login\": \"kszucs\",\n+ \"node_id\": \"MDQ6VXNlcjk2MTc0Nw==\",\n+ \"organizations_url\": \"https://api.github.com/users/kszucs/orgs\",\n+ \"received_events_url\": \"https://api.github.com/users/kszucs/received_events\",\n+ \"repos_url\": \"https://api.github.com/users/kszucs/repos\",\n+ \"site_admin\": false,\n+ \"starred_url\": \"https://api.github.com/users/kszucs/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/kszucs/subscriptions\",\n+ \"type\": \"User\",\n+ \"url\": \"https://api.github.com/users/kszucs\"\n+ }\n+ },\n+ \"issue\": {\n+ \"assignee\": null,\n+ \"assignees\": [],\n+ \"author_association\": \"MEMBER\",\n+ \"body\": \"\",\n+ \"closed_at\": null,\n+ \"comments\": 1,\n+ \"comments_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26/comments\",\n+ \"created_at\": \"2019-04-05T11:22:15Z\",\n+ \"events_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26/events\",\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot/pull/26\",\n+ \"id\": 429706959,\n+ \"labels\": [],\n+ \"labels_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26/labels{/name}\",\n+ \"locked\": false,\n+ \"milestone\": null,\n+ \"node_id\": \"MDExOlB1bGxSZXF1ZXN0MjY3Nzg1NTUy\",\n+ \"number\": 26,\n+ \"pull_request\": {\n+ \"diff_url\": \"https://github.com/ursa-labs/ursabot/pull/26.diff\",\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot/pull/26\",\n+ \"patch_url\": \"https://github.com/ursa-labs/ursabot/pull/26.patch\",\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot/pulls/26\"\n+ },\n+ \"repository_url\": \"https://api.github.com/repos/ursa-labs/ursabot\",\n+ \"state\": \"open\",\n+ \"title\": \"Unittests for GithubHook\",\n+ \"updated_at\": \"2019-04-05T11:35:46Z\",\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26\",\n+ \"user\": {\n+ \"avatar_url\": \"https://avatars1.githubusercontent.com/u/961747?v=4\",\n+ \"events_url\": \"https://api.github.com/users/kszucs/events{/privacy}\",\n+ \"followers_url\": \"https://api.github.com/users/kszucs/followers\",\n+ \"following_url\": \"https://api.github.com/users/kszucs/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/kszucs/gists{/gist_id}\",\n+ \"gravatar_id\": \"\",\n+ \"html_url\": \"https://github.com/kszucs\",\n+ \"id\": 961747,\n+ \"login\": \"kszucs\",\n+ \"node_id\": \"MDQ6VXNlcjk2MTc0Nw==\",\n+ \"organizations_url\": \"https://api.github.com/users/kszucs/orgs\",\n+ \"received_events_url\": \"https://api.github.com/users/kszucs/received_events\",\n+ \"repos_url\": \"https://api.github.com/users/kszucs/repos\",\n+ \"site_admin\": false,\n+ \"starred_url\": \"https://api.github.com/users/kszucs/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/kszucs/subscriptions\",\n+ \"type\": \"User\",\n+ \"url\": \"https://api.github.com/users/kszucs\"\n+ }\n+ },\n+ \"organization\": {\n+ \"avatar_url\": \"https://avatars2.githubusercontent.com/u/46514972?v=4\",\n+ \"description\": \"Innovation lab for open source data science tools, powered by Apache Arrow\",\n+ \"events_url\": \"https://api.github.com/orgs/ursa-labs/events\",\n+ \"hooks_url\": \"https://api.github.com/orgs/ursa-labs/hooks\",\n+ \"id\": 46514972,\n+ \"issues_url\": \"https://api.github.com/orgs/ursa-labs/issues\",\n+ \"login\": \"ursa-labs\",\n+ \"members_url\": \"https://api.github.com/orgs/ursa-labs/members{/member}\",\n+ \"node_id\": \"MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy\",\n+ \"public_members_url\": \"https://api.github.com/orgs/ursa-labs/public_members{/member}\",\n+ \"repos_url\": \"https://api.github.com/orgs/ursa-labs/repos\",\n+ \"url\": \"https://api.github.com/orgs/ursa-labs\"\n+ },\n+ \"repository\": {\n+ \"archive_url\": \"https://api.github.com/repos/ursa-labs/ursabot/{archive_format}{/ref}\",\n+ \"archived\": false,\n+ \"assignees_url\": \"https://api.github.com/repos/ursa-labs/ursabot/assignees{/user}\",\n+ \"blobs_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/blobs{/sha}\",\n+ \"branches_url\": \"https://api.github.com/repos/ursa-labs/ursabot/branches{/branch}\",\n+ \"clone_url\": \"https://github.com/ursa-labs/ursabot.git\",\n+ \"collaborators_url\": \"https://api.github.com/repos/ursa-labs/ursabot/collaborators{/collaborator}\",\n+ \"comments_url\": \"https://api.github.com/repos/ursa-labs/ursabot/comments{/number}\",\n+ \"commits_url\": \"https://api.github.com/repos/ursa-labs/ursabot/commits{/sha}\",\n+ \"compare_url\": \"https://api.github.com/repos/ursa-labs/ursabot/compare/{base}...{head}\",\n+ \"contents_url\": \"https://api.github.com/repos/ursa-labs/ursabot/contents/{+path}\",\n+ \"contributors_url\": \"https://api.github.com/repos/ursa-labs/ursabot/contributors\",\n+ \"created_at\": \"2019-02-04T15:40:31Z\",\n+ \"default_branch\": \"master\",\n+ \"deployments_url\": \"https://api.github.com/repos/ursa-labs/ursabot/deployments\",\n+ \"description\": null,\n+ \"disabled\": false,\n+ \"downloads_url\": \"https://api.github.com/repos/ursa-labs/ursabot/downloads\",\n+ \"events_url\": \"https://api.github.com/repos/ursa-labs/ursabot/events\",\n+ \"fork\": false,\n+ \"forks\": 0,\n+ \"forks_count\": 0,\n+ \"forks_url\": \"https://api.github.com/repos/ursa-labs/ursabot/forks\",\n+ \"full_name\": \"ursa-labs/ursabot\",\n+ \"git_commits_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/commits{/sha}\",\n+ \"git_refs_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/refs{/sha}\",\n+ \"git_tags_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/tags{/sha}\",\n+ \"git_url\": \"git://github.com/ursa-labs/ursabot.git\",\n+ \"has_downloads\": true,\n+ \"has_issues\": true,\n+ \"has_pages\": false,\n+ \"has_projects\": true,\n+ \"has_wiki\": true,\n+ \"homepage\": null,\n+ \"hooks_url\": \"https://api.github.com/repos/ursa-labs/ursabot/hooks\",\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot\",\n+ \"id\": 169101701,\n+ \"issue_comment_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/comments{/number}\",\n+ \"issue_events_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/events{/number}\",\n+ \"issues_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues{/number}\",\n+ \"keys_url\": \"https://api.github.com/repos/ursa-labs/ursabot/keys{/key_id}\",\n+ \"labels_url\": \"https://api.github.com/repos/ursa-labs/ursabot/labels{/name}\",\n+ \"language\": \"Jupyter Notebook\",\n+ \"languages_url\": \"https://api.github.com/repos/ursa-labs/ursabot/languages\",\n+ \"license\": null,\n+ \"merges_url\": \"https://api.github.com/repos/ursa-labs/ursabot/merges\",\n+ \"milestones_url\": \"https://api.github.com/repos/ursa-labs/ursabot/milestones{/number}\",\n+ \"mirror_url\": null,\n+ \"name\": \"ursabot\",\n+ \"node_id\": \"MDEwOlJlcG9zaXRvcnkxNjkxMDE3MDE=\",\n+ \"notifications_url\": \"https://api.github.com/repos/ursa-labs/ursabot/notifications{?since,all,participating}\",\n+ \"open_issues\": 19,\n+ \"open_issues_count\": 19,\n+ \"owner\": {\n+ \"avatar_url\": \"https://avatars2.githubusercontent.com/u/46514972?v=4\",\n+ \"events_url\": \"https://api.github.com/users/ursa-labs/events{/privacy}\",\n+ \"followers_url\": \"https://api.github.com/users/ursa-labs/followers\",\n+ \"following_url\": \"https://api.github.com/users/ursa-labs/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/ursa-labs/gists{/gist_id}\",\n+ \"gravatar_id\": \"\",\n+ \"html_url\": \"https://github.com/ursa-labs\",\n+ \"id\": 46514972,\n+ \"login\": \"ursa-labs\",\n+ \"node_id\": \"MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy\",\n+ \"organizations_url\": \"https://api.github.com/users/ursa-labs/orgs\",\n+ \"received_events_url\": \"https://api.github.com/users/ursa-labs/received_events\",\n+ \"repos_url\": \"https://api.github.com/users/ursa-labs/repos\",\n+ \"site_admin\": false,\n+ \"starred_url\": \"https://api.github.com/users/ursa-labs/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/ursa-labs/subscriptions\",\n+ \"type\": \"Organization\",\n+ \"url\": \"https://api.github.com/users/ursa-labs\"\n+ },\n+ \"private\": false,\n+ \"pulls_url\": \"https://api.github.com/repos/ursa-labs/ursabot/pulls{/number}\",\n+ \"pushed_at\": \"2019-04-05T11:22:16Z\",\n+ \"releases_url\": \"https://api.github.com/repos/ursa-labs/ursabot/releases{/id}\",\n+ \"size\": 892,\n+ \"ssh_url\": \"git@github.com:ursa-labs/ursabot.git\",\n+ \"stargazers_count\": 1,\n+ \"stargazers_url\": \"https://api.github.com/repos/ursa-labs/ursabot/stargazers\",\n+ \"statuses_url\": \"https://api.github.com/repos/ursa-labs/ursabot/statuses/{sha}\",\n+ \"subscribers_url\": \"https://api.github.com/repos/ursa-labs/ursabot/subscribers\",\n+ \"subscription_url\": \"https://api.github.com/repos/ursa-labs/ursabot/subscription\",\n+ \"svn_url\": \"https://github.com/ursa-labs/ursabot\",\n+ \"tags_url\": \"https://api.github.com/repos/ursa-labs/ursabot/tags\",\n+ \"teams_url\": \"https://api.github.com/repos/ursa-labs/ursabot/teams\",\n+ \"trees_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/trees{/sha}\",\n+ \"updated_at\": \"2019-04-04T17:49:10Z\",\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot\",\n+ \"watchers\": 1,\n+ \"watchers_count\": 1\n+ },\n+ \"sender\": {\n+ \"avatar_url\": \"https://avatars1.githubusercontent.com/u/961747?v=4\",\n+ \"events_url\": \"https://api.github.com/users/kszucs/events{/privacy}\",\n+ \"followers_url\": \"https://api.github.com/users/kszucs/followers\",\n+ \"following_url\": \"https://api.github.com/users/kszucs/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/kszucs/gists{/gist_id}\",\n+ \"gravatar_id\": \"\",\n+ \"html_url\": \"https://github.com/kszucs\",\n+ \"id\": 961747,\n+ \"login\": \"kszucs\",\n+ \"node_id\": \"MDQ6VXNlcjk2MTc0Nw==\",\n+ \"organizations_url\": \"https://api.github.com/users/kszucs/orgs\",\n+ \"received_events_url\": \"https://api.github.com/users/kszucs/received_events\",\n+ \"repos_url\": \"https://api.github.com/users/kszucs/repos\",\n+ \"site_admin\": false,\n+ \"starred_url\": \"https://api.github.com/users/kszucs/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/kszucs/subscriptions\",\n+ \"type\": \"User\",\n+ \"url\": \"https://api.github.com/users/kszucs\"\n+ }\n+}"
- },
- {
- "sha": "80ff46510a2f39ae60f7c3a98e5fdaef8e688784",
- "filename": "ursabot/tests/fixtures/issue-comment-without-pull-request.json",
- "status": "added",
- "additions": 206,
- "deletions": 0,
- "changes": 206,
- "blob_url": "https://github.com/ursa-labs/ursabot/blob/2705da2b616b98fa6010a25813c5a7a27456f71d/ursabot/tests/fixtures/issue-comment-without-pull-request.json",
- "raw_url": "https://github.com/ursa-labs/ursabot/raw/2705da2b616b98fa6010a25813c5a7a27456f71d/ursabot/tests/fixtures/issue-comment-without-pull-request.json",
- "contents_url": "https://api.github.com/repos/ursa-labs/ursabot/contents/ursabot/tests/fixtures/issue-comment-without-pull-request.json?ref=2705da2b616b98fa6010a25813c5a7a27456f71d",
- "patch": "@@ -0,0 +1,206 @@\n+{\n+ \"action\": \"created\",\n+ \"comment\": {\n+ \"author_association\": \"NONE\",\n+ \"body\": \"Ursabot only listens to pull request comments!\",\n+ \"created_at\": \"2019-04-05T11:53:43Z\",\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot/issues/19#issuecomment-480248217\",\n+ \"id\": 480248217,\n+ \"issue_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/19\",\n+ \"node_id\": \"MDEyOklzc3VlQ29tbWVudDQ4MDI0ODIxNw==\",\n+ \"updated_at\": \"2019-04-05T11:53:43Z\",\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/comments/480248217\",\n+ \"user\": {\n+ \"avatar_url\": \"https://avatars2.githubusercontent.com/u/49275095?v=4\",\n+ \"events_url\": \"https://api.github.com/users/ursabot/events{/privacy}\",\n+ \"followers_url\": \"https://api.github.com/users/ursabot/followers\",\n+ \"following_url\": \"https://api.github.com/users/ursabot/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/ursabot/gists{/gist_id}\",\n+ \"gravatar_id\": \"\",\n+ \"html_url\": \"https://github.com/ursabot\",\n+ \"id\": 49275095,\n+ \"login\": \"ursabot\",\n+ \"node_id\": \"MDQ6VXNlcjQ5Mjc1MDk1\",\n+ \"organizations_url\": \"https://api.github.com/users/ursabot/orgs\",\n+ \"received_events_url\": \"https://api.github.com/users/ursabot/received_events\",\n+ \"repos_url\": \"https://api.github.com/users/ursabot/repos\",\n+ \"site_admin\": false,\n+ \"starred_url\": \"https://api.github.com/users/ursabot/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/ursabot/subscriptions\",\n+ \"type\": \"User\",\n+ \"url\": \"https://api.github.com/users/ursabot\"\n+ }\n+ },\n+ \"issue\": {\n+ \"assignee\": null,\n+ \"assignees\": [],\n+ \"author_association\": \"MEMBER\",\n+ \"body\": \"\",\n+ \"closed_at\": null,\n+ \"comments\": 4,\n+ \"comments_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/19/comments\",\n+ \"created_at\": \"2019-04-02T09:56:41Z\",\n+ \"events_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/19/events\",\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot/issues/19\",\n+ \"id\": 428131685,\n+ \"labels\": [],\n+ \"labels_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/19/labels{/name}\",\n+ \"locked\": false,\n+ \"milestone\": null,\n+ \"node_id\": \"MDU6SXNzdWU0MjgxMzE2ODU=\",\n+ \"number\": 19,\n+ \"repository_url\": \"https://api.github.com/repos/ursa-labs/ursabot\",\n+ \"state\": \"open\",\n+ \"title\": \"Build ursabot itself via ursabot\",\n+ \"updated_at\": \"2019-04-05T11:53:43Z\",\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/19\",\n+ \"user\": {\n+ \"avatar_url\": \"https://avatars1.githubusercontent.com/u/961747?v=4\",\n+ \"events_url\": \"https://api.github.com/users/kszucs/events{/privacy}\",\n+ \"followers_url\": \"https://api.github.com/users/kszucs/followers\",\n+ \"following_url\": \"https://api.github.com/users/kszucs/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/kszucs/gists{/gist_id}\",\n+ \"gravatar_id\": \"\",\n+ \"html_url\": \"https://github.com/kszucs\",\n+ \"id\": 961747,\n+ \"login\": \"kszucs\",\n+ \"node_id\": \"MDQ6VXNlcjk2MTc0Nw==\",\n+ \"organizations_url\": \"https://api.github.com/users/kszucs/orgs\",\n+ \"received_events_url\": \"https://api.github.com/users/kszucs/received_events\",\n+ \"repos_url\": \"https://api.github.com/users/kszucs/repos\",\n+ \"site_admin\": false,\n+ \"starred_url\": \"https://api.github.com/users/kszucs/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/kszucs/subscriptions\",\n+ \"type\": \"User\",\n+ \"url\": \"https://api.github.com/users/kszucs\"\n+ }\n+ },\n+ \"organization\": {\n+ \"avatar_url\": \"https://avatars2.githubusercontent.com/u/46514972?v=4\",\n+ \"description\": \"Innovation lab for open source data science tools, powered by Apache Arrow\",\n+ \"events_url\": \"https://api.github.com/orgs/ursa-labs/events\",\n+ \"hooks_url\": \"https://api.github.com/orgs/ursa-labs/hooks\",\n+ \"id\": 46514972,\n+ \"issues_url\": \"https://api.github.com/orgs/ursa-labs/issues\",\n+ \"login\": \"ursa-labs\",\n+ \"members_url\": \"https://api.github.com/orgs/ursa-labs/members{/member}\",\n+ \"node_id\": \"MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy\",\n+ \"public_members_url\": \"https://api.github.com/orgs/ursa-labs/public_members{/member}\",\n+ \"repos_url\": \"https://api.github.com/orgs/ursa-labs/repos\",\n+ \"url\": \"https://api.github.com/orgs/ursa-labs\"\n+ },\n+ \"repository\": {\n+ \"archive_url\": \"https://api.github.com/repos/ursa-labs/ursabot/{archive_format}{/ref}\",\n+ \"archived\": false,\n+ \"assignees_url\": \"https://api.github.com/repos/ursa-labs/ursabot/assignees{/user}\",\n+ \"blobs_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/blobs{/sha}\",\n+ \"branches_url\": \"https://api.github.com/repos/ursa-labs/ursabot/branches{/branch}\",\n+ \"clone_url\": \"https://github.com/ursa-labs/ursabot.git\",\n+ \"collaborators_url\": \"https://api.github.com/repos/ursa-labs/ursabot/collaborators{/collaborator}\",\n+ \"comments_url\": \"https://api.github.com/repos/ursa-labs/ursabot/comments{/number}\",\n+ \"commits_url\": \"https://api.github.com/repos/ursa-labs/ursabot/commits{/sha}\",\n+ \"compare_url\": \"https://api.github.com/repos/ursa-labs/ursabot/compare/{base}...{head}\",\n+ \"contents_url\": \"https://api.github.com/repos/ursa-labs/ursabot/contents/{+path}\",\n+ \"contributors_url\": \"https://api.github.com/repos/ursa-labs/ursabot/contributors\",\n+ \"created_at\": \"2019-02-04T15:40:31Z\",\n+ \"default_branch\": \"master\",\n+ \"deployments_url\": \"https://api.github.com/repos/ursa-labs/ursabot/deployments\",\n+ \"description\": null,\n+ \"disabled\": false,\n+ \"downloads_url\": \"https://api.github.com/repos/ursa-labs/ursabot/downloads\",\n+ \"events_url\": \"https://api.github.com/repos/ursa-labs/ursabot/events\",\n+ \"fork\": false,\n+ \"forks\": 0,\n+ \"forks_count\": 0,\n+ \"forks_url\": \"https://api.github.com/repos/ursa-labs/ursabot/forks\",\n+ \"full_name\": \"ursa-labs/ursabot\",\n+ \"git_commits_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/commits{/sha}\",\n+ \"git_refs_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/refs{/sha}\",\n+ \"git_tags_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/tags{/sha}\",\n+ \"git_url\": \"git://github.com/ursa-labs/ursabot.git\",\n+ \"has_downloads\": true,\n+ \"has_issues\": true,\n+ \"has_pages\": false,\n+ \"has_projects\": true,\n+ \"has_wiki\": true,\n+ \"homepage\": null,\n+ \"hooks_url\": \"https://api.github.com/repos/ursa-labs/ursabot/hooks\",\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot\",\n+ \"id\": 169101701,\n+ \"issue_comment_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/comments{/number}\",\n+ \"issue_events_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/events{/number}\",\n+ \"issues_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues{/number}\",\n+ \"keys_url\": \"https://api.github.com/repos/ursa-labs/ursabot/keys{/key_id}\",\n+ \"labels_url\": \"https://api.github.com/repos/ursa-labs/ursabot/labels{/name}\",\n+ \"language\": \"Jupyter Notebook\",\n+ \"languages_url\": \"https://api.github.com/repos/ursa-labs/ursabot/languages\",\n+ \"license\": null,\n+ \"merges_url\": \"https://api.github.com/repos/ursa-labs/ursabot/merges\",\n+ \"milestones_url\": \"https://api.github.com/repos/ursa-labs/ursabot/milestones{/number}\",\n+ \"mirror_url\": null,\n+ \"name\": \"ursabot\",\n+ \"node_id\": \"MDEwOlJlcG9zaXRvcnkxNjkxMDE3MDE=\",\n+ \"notifications_url\": \"https://api.github.com/repos/ursa-labs/ursabot/notifications{?since,all,participating}\",\n+ \"open_issues\": 19,\n+ \"open_issues_count\": 19,\n+ \"owner\": {\n+ \"avatar_url\": \"https://avatars2.githubusercontent.com/u/46514972?v=4\",\n+ \"events_url\": \"https://api.github.com/users/ursa-labs/events{/privacy}\",\n+ \"followers_url\": \"https://api.github.com/users/ursa-labs/followers\",\n+ \"following_url\": \"https://api.github.com/users/ursa-labs/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/ursa-labs/gists{/gist_id}\",\n+ \"gravatar_id\": \"\",\n+ \"html_url\": \"https://github.com/ursa-labs\",\n+ \"id\": 46514972,\n+ \"login\": \"ursa-labs\",\n+ \"node_id\": \"MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy\",\n+ \"organizations_url\": \"https://api.github.com/users/ursa-labs/orgs\",\n+ \"received_events_url\": \"https://api.github.com/users/ursa-labs/received_events\",\n+ \"repos_url\": \"https://api.github.com/users/ursa-labs/repos\",\n+ \"site_admin\": false,\n+ \"starred_url\": \"https://api.github.com/users/ursa-labs/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/ursa-labs/subscriptions\",\n+ \"type\": \"Organization\",\n+ \"url\": \"https://api.github.com/users/ursa-labs\"\n+ },\n+ \"private\": false,\n+ \"pulls_url\": \"https://api.github.com/repos/ursa-labs/ursabot/pulls{/number}\",\n+ \"pushed_at\": \"2019-04-05T11:22:16Z\",\n+ \"releases_url\": \"https://api.github.com/repos/ursa-labs/ursabot/releases{/id}\",\n+ \"size\": 892,\n+ \"ssh_url\": \"git@github.com:ursa-labs/ursabot.git\",\n+ \"stargazers_count\": 1,\n+ \"stargazers_url\": \"https://api.github.com/repos/ursa-labs/ursabot/stargazers\",\n+ \"statuses_url\": \"https://api.github.com/repos/ursa-labs/ursabot/statuses/{sha}\",\n+ \"subscribers_url\": \"https://api.github.com/repos/ursa-labs/ursabot/subscribers\",\n+ \"subscription_url\": \"https://api.github.com/repos/ursa-labs/ursabot/subscription\",\n+ \"svn_url\": \"https://github.com/ursa-labs/ursabot\",\n+ \"tags_url\": \"https://api.github.com/repos/ursa-labs/ursabot/tags\",\n+ \"teams_url\": \"https://api.github.com/repos/ursa-labs/ursabot/teams\",\n+ \"trees_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/trees{/sha}\",\n+ \"updated_at\": \"2019-04-04T17:49:10Z\",\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot\",\n+ \"watchers\": 1,\n+ \"watchers_count\": 1\n+ },\n+ \"sender\": {\n+ \"avatar_url\": \"https://avatars2.githubusercontent.com/u/49275095?v=4\",\n+ \"events_url\": \"https://api.github.com/users/ursabot/events{/privacy}\",\n+ \"followers_url\": \"https://api.github.com/users/ursabot/followers\",\n+ \"following_url\": \"https://api.github.com/users/ursabot/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/ursabot/gists{/gist_id}\",\n+ \"gravatar_id\": \"\",\n+ \"html_url\": \"https://github.com/ursabot\",\n+ \"id\": 49275095,\n+ \"login\": \"ursabot\",\n+ \"node_id\": \"MDQ6VXNlcjQ5Mjc1MDk1\",\n+ \"organizations_url\": \"https://api.github.com/users/ursabot/orgs\",\n+ \"received_events_url\": \"https://api.github.com/users/ursabot/received_events\",\n+ \"repos_url\": \"https://api.github.com/users/ursabot/repos\",\n+ \"site_admin\": false,\n+ \"starred_url\": \"https://api.github.com/users/ursabot/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/ursabot/subscriptions\",\n+ \"type\": \"User\",\n+ \"url\": \"https://api.github.com/users/ursabot\"\n+ }\n+}"
- },
- {
- "sha": "c738bb0eb54c87ba0f23e97e827d77c2be74d0b6",
- "filename": "ursabot/tests/test_hooks.py",
- "status": "modified",
- "additions": 4,
- "deletions": 4,
- "changes": 8,
- "blob_url": "https://github.com/ursa-labs/ursabot/blob/2705da2b616b98fa6010a25813c5a7a27456f71d/ursabot/tests/test_hooks.py",
- "raw_url": "https://github.com/ursa-labs/ursabot/raw/2705da2b616b98fa6010a25813c5a7a27456f71d/ursabot/tests/test_hooks.py",
- "contents_url": "https://api.github.com/repos/ursa-labs/ursabot/contents/ursabot/tests/test_hooks.py?ref=2705da2b616b98fa6010a25813c5a7a27456f71d",
- "patch": "@@ -54,7 +54,7 @@ class TestGithubHook(ChangeHookTestCase):\n await self.request('ping', {})\n assert len(self.hook.master.data.updates.changesAdded) == 0\n \n- @ensure_deferred\n- async def test_issue_comment(self):\n- payload = {}\n- await self.request('issue_comment', payload)\n+ # @ensure_deferred\n+ # async def test_issue_comment(self):\n+ # payload = {}\n+ # await self.request('issue_comment', payload)"
- }
- ]
-}
\ No newline at end of file
diff --git a/dev/archery/archery/tests/fixtures/pull-request-26-files.json b/dev/archery/archery/tests/fixtures/pull-request-26-files.json
deleted file mode 100644
index b039b3d..0000000
--- a/dev/archery/archery/tests/fixtures/pull-request-26-files.json
+++ /dev/null
@@ -1,170 +0,0 @@
-[
- {
- "sha": "ebfe3f6c5e98723f9751c99ce8ce798f1ba529c5",
- "filename": ".travis.yml",
- "status": "modified",
- "additions": 4,
- "deletions": 1,
- "changes": 5,
- "blob_url": "https://github.com/ursa-labs/ursabot/blob/70267dee34884e4b972388e1b30d57f6248c58d0/.travis.yml",
- "raw_url": "https://github.com/ursa-labs/ursabot/raw/70267dee34884e4b972388e1b30d57f6248c58d0/.travis.yml",
- "contents_url": "https://api.github.com/repos/ursa-labs/ursabot/contents/.travis.yml?ref=70267dee34884e4b972388e1b30d57f6248c58d0",
- "patch": "@@ -4,7 +4,10 @@ services:\n python:\n - 3.6\n script:\n- - pip install \"pytest>=3.9\" flake8 -e .\n+ # --no-binary buildbot is required because buildbot doesn't bundle its tests\n+ # to binary wheels, but ursabot's test suite depends on buildbot's so install\n+ # it from source\n+ - pip install --no-binary buildbot \"pytest>=3.9\" mock flake8 -e .\n \n # run linter\n - flake8 ursabot"
- },
- {
- "sha": "86ad809d3f74c175b92ac58c6c645b0fbf5fa2c5",
- "filename": "setup.py",
- "status": "modified",
- "additions": 6,
- "deletions": 1,
- "changes": 7,
- "blob_url": "https://github.com/ursa-labs/ursabot/blob/70267dee34884e4b972388e1b30d57f6248c58d0/setup.py",
- "raw_url": "https://github.com/ursa-labs/ursabot/raw/70267dee34884e4b972388e1b30d57f6248c58d0/setup.py",
- "contents_url": "https://api.github.com/repos/ursa-labs/ursabot/contents/setup.py?ref=70267dee34884e4b972388e1b30d57f6248c58d0",
- "patch": "@@ -1,8 +1,13 @@\n #!/usr/bin/env python\n \n+import sys\n from setuptools import setup\n \n \n+if sys.version_info < (3, 6):\n+ sys.exit('Python < 3.6 is not supported due to missing asyncio support')\n+\n+\n # TODO(kszucs): add package data, change maintainer\n setup(\n name='ursabot',\n@@ -15,7 +20,7 @@\n setup_requires=['setuptools_scm'],\n install_requires=['click', 'dask', 'docker', 'docker-map', 'toolz',\n 'buildbot', 'treq'],\n- tests_require=['pytest>=3.9'],\n+ tests_require=['pytest>=3.9', 'mock'],\n entry_points='''\n [console_scripts]\n ursabot=ursabot.cli:ursabot"
- },
- {
- "sha": "c884f3f85bba499d77d9ad28bcd0ff5edf80f957",
- "filename": "ursabot/factories.py",
- "status": "modified",
- "additions": 6,
- "deletions": 2,
- "changes": 8,
- "blob_url": "https://github.com/ursa-labs/ursabot/blob/70267dee34884e4b972388e1b30d57f6248c58d0/ursabot/factories.py",
- "raw_url": "https://github.com/ursa-labs/ursabot/raw/70267dee34884e4b972388e1b30d57f6248c58d0/ursabot/factories.py",
- "contents_url": "https://api.github.com/repos/ursa-labs/ursabot/contents/ursabot/factories.py?ref=70267dee34884e4b972388e1b30d57f6248c58d0",
- "patch": "@@ -79,8 +79,12 @@ def prepend_step(self, step):\n repourl='https://github.com/ursa-labs/ursabot',\n mode='full'),\n ShellCommand(command=['ls', '-lah']),\n- ShellCommand(command=['pip', 'install', 'pytest', 'flake8']),\n- ShellCommand(command=['pip', 'install', '-e', '.']),\n+ ShellCommand(command=['pip', 'install', 'pytest', 'flake8', 'mock']),\n+ # --no-binary buildbot is required because buildbot doesn't bundle its\n+ # tests to binary wheels, but ursabot's test suite depends on buildbot's\n+ # so install it from source\n+ ShellCommand(command=['pip', 'install', '--no-binary', 'buildbot',\n+ '-e', '.']),\n ShellCommand(command=['flake8']),\n ShellCommand(command=['pytest', '-v', '-m', 'not docker', 'ursabot']),\n ShellCommand(command=['buildbot', 'checkconfig', '.'])"
- },
- {
- "sha": "0265cfbd9c2882f492469882a7bf513a1c1b5af4",
- "filename": "ursabot/hooks.py",
- "status": "modified",
- "additions": 17,
- "deletions": 19,
- "changes": 36,
- "blob_url": "https://github.com/ursa-labs/ursabot/blob/70267dee34884e4b972388e1b30d57f6248c58d0/ursabot/hooks.py",
- "raw_url": "https://github.com/ursa-labs/ursabot/raw/70267dee34884e4b972388e1b30d57f6248c58d0/ursabot/hooks.py",
- "contents_url": "https://api.github.com/repos/ursa-labs/ursabot/contents/ursabot/hooks.py?ref=70267dee34884e4b972388e1b30d57f6248c58d0",
- "patch": "@@ -1,11 +1,11 @@\n from urllib.parse import urlparse\n \n from twisted.python import log\n-from twisted.internet import defer\n \n from buildbot.www.hooks.github import GitHubEventHandler\n from buildbot.util.httpclientservice import HTTPClientService\n \n+from .utils import ensure_deferred\n \n BOTNAME = 'ursabot'\n \n@@ -22,20 +22,18 @@ def _client(self):\n self.master, self.github_api_endpoint, headers=headers,\n debug=self.debug, verify=self.verify)\n \n- @defer.inlineCallbacks\n- def _get(self, url):\n+ async def _get(self, url):\n url = urlparse(url)\n- client = yield self._client()\n- response = yield client.get(url.path)\n- result = yield response.json()\n+ client = await self._client()\n+ response = await client.get(url.path)\n+ result = await response.json()\n return result\n \n- @defer.inlineCallbacks\n- def _post(self, url, data):\n+ async def _post(self, url, data):\n url = urlparse(url)\n- client = yield self._client()\n- response = yield client.post(url.path, json=data)\n- result = yield response.json()\n+ client = await self._client()\n+ response = await client.post(url.path, json=data)\n+ result = await response.json()\n log.msg(f'POST to {url} with the following result: {result}')\n return result\n \n@@ -46,8 +44,8 @@ def _parse_command(self, message):\n return message.split(mention)[-1].lower().strip()\n return None\n \n- @defer.inlineCallbacks\n- def handle_issue_comment(self, payload, event):\n+ @ensure_deferred\n+ async def handle_issue_comment(self, payload, event):\n issue = payload['issue']\n comments_url = issue['comments_url']\n command = self._parse_command(payload['comment']['body'])\n@@ -64,16 +62,16 @@ def handle_issue_comment(self, payload, event):\n elif command == 'build':\n if 'pull_request' not in issue:\n message = 'Ursabot only listens to pull request comments!'\n- yield self._post(comments_url, {'body': message})\n+ await self._post(comments_url, {'body': message})\n return [], 'git'\n else:\n message = f'Unknown command \"{command}\"'\n- yield self._post(comments_url, {'body': message})\n+ await self._post(comments_url, {'body': message})\n return [], 'git'\n \n try:\n- pull_request = yield self._get(issue['pull_request']['url'])\n- changes, _ = yield self.handle_pull_request({\n+ pull_request = await self._get(issue['pull_request']['url'])\n+ changes, _ = await self.handle_pull_request({\n 'action': 'synchronize',\n 'sender': payload['sender'],\n 'repository': payload['repository'],\n@@ -82,11 +80,11 @@ def handle_issue_comment(self, payload, event):\n }, event)\n except Exception as e:\n message = \"I've failed to start builds for this PR\"\n- yield self._post(comments_url, {'body': message})\n+ await self._post(comments_url, {'body': message})\n raise e\n else:\n message = \"I've successfully started builds for this PR\"\n- yield self._post(comments_url, {'body': message})\n+ await self._post(comments_url, {'body': message})\n return changes, 'git'\n \n # TODO(kszucs):"
- },
- {
- "sha": "1e1ecf2ce47da929dbf1b93632640e7e6ae1cfe0",
- "filename": "ursabot/steps.py",
- "status": "modified",
- "additions": 13,
- "deletions": 13,
- "changes": 26,
- "blob_url": "https://github.com/ursa-labs/ursabot/blob/70267dee34884e4b972388e1b30d57f6248c58d0/ursabot/steps.py",
- "raw_url": "https://github.com/ursa-labs/ursabot/raw/70267dee34884e4b972388e1b30d57f6248c58d0/ursabot/steps.py",
- "contents_url": "https://api.github.com/repos/ursa-labs/ursabot/contents/ursabot/steps.py?ref=70267dee34884e4b972388e1b30d57f6248c58d0",
- "patch": "@@ -1,9 +1,9 @@\n-from twisted.internet import defer\n-\n from buildbot.plugins import steps, util\n from buildbot.process import buildstep\n from buildbot.process.results import SUCCESS\n \n+from .utils import ensure_deferred\n+\n \n class ShellMixin(buildstep.ShellMixin):\n \"\"\"Run command in a login bash shell\n@@ -49,10 +49,10 @@ def __init__(self, **kwargs):\n kwargs = self.setupShellMixin(kwargs)\n super().__init__(**kwargs)\n \n- @defer.inlineCallbacks\n- def run(self):\n- cmd = yield self.makeRemoteShellCommand(command=self.command)\n- yield self.runCommand(cmd)\n+ @ensure_deferred\n+ async def run(self):\n+ cmd = await self.makeRemoteShellCommand(command=self.command)\n+ await self.runCommand(cmd)\n return cmd.results()\n \n \n@@ -71,8 +71,8 @@ class CMake(ShellMixin, steps.CMake):\n \n name = 'CMake'\n \n- @defer.inlineCallbacks\n- def run(self):\n+ @ensure_deferred\n+ async def run(self):\n \"\"\"Create and run CMake command\n \n Copied from the original CMake implementation to handle None values as\n@@ -94,8 +94,8 @@ def run(self):\n if self.options is not None:\n command.extend(self.options)\n \n- cmd = yield self.makeRemoteShellCommand(command=command)\n- yield self.runCommand(cmd)\n+ cmd = await self.makeRemoteShellCommand(command=command)\n+ await self.runCommand(cmd)\n \n return cmd.results()\n \n@@ -117,8 +117,8 @@ def __init__(self, variables, source='WorkerEnvironment', **kwargs):\n self.source = source\n super().__init__(**kwargs)\n \n- @defer.inlineCallbacks\n- def run(self):\n+ @ensure_deferred\n+ async def run(self):\n # on Windows, environment variables are case-insensitive, but we have\n # a case-sensitive dictionary in worker_environ. Fortunately, that\n # dictionary is also folded to uppercase, so we can simply fold the\n@@ -139,7 +139,7 @@ def run(self):\n # TODO(kszucs) try with self.setProperty similarly like in\n # SetProperties\n properties.setProperty(prop, value, self.source, runtime=True)\n- yield self.addCompleteLog('set-prop', f'{prop}: {value}')\n+ await self.addCompleteLog('set-prop', f'{prop}: {value}')\n \n return SUCCESS\n "
- },
- {
- "sha": "6a7d5308be6608f542a810d410f9240157a1340f",
- "filename": "ursabot/tests/fixtures/issue-comment-build-command.json",
- "status": "added",
- "additions": 212,
- "deletions": 0,
- "changes": 212,
- "blob_url": "https://github.com/ursa-labs/ursabot/blob/70267dee34884e4b972388e1b30d57f6248c58d0/ursabot/tests/fixtures/issue-comment-build-command.json",
- "raw_url": "https://github.com/ursa-labs/ursabot/raw/70267dee34884e4b972388e1b30d57f6248c58d0/ursabot/tests/fixtures/issue-comment-build-command.json",
- "contents_url": "https://api.github.com/repos/ursa-labs/ursabot/contents/ursabot/tests/fixtures/issue-comment-build-command.json?ref=70267dee34884e4b972388e1b30d57f6248c58d0",
- "patch": "@@ -0,0 +1,212 @@\n+{\n+ \"action\": \"created\",\n+ \"comment\": {\n+ \"author_association\": \"MEMBER\",\n+ \"body\": \"@ursabot build\",\n+ \"created_at\": \"2019-04-05T11:55:43Z\",\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot/pull/26#issuecomment-480248726\",\n+ \"id\": 480248726,\n+ \"issue_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26\",\n+ \"node_id\": \"MDEyOklzc3VlQ29tbWVudDQ4MDI0ODcyNg==\",\n+ \"updated_at\": \"2019-04-05T11:55:43Z\",\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/comments/480248726\",\n+ \"user\": {\n+ \"avatar_url\": \"https://avatars1.githubusercontent.com/u/961747?v=4\",\n+ \"events_url\": \"https://api.github.com/users/kszucs/events{/privacy}\",\n+ \"followers_url\": \"https://api.github.com/users/kszucs/followers\",\n+ \"following_url\": \"https://api.github.com/users/kszucs/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/kszucs/gists{/gist_id}\",\n+ \"gravatar_id\": \"\",\n+ \"html_url\": \"https://github.com/kszucs\",\n+ \"id\": 961747,\n+ \"login\": \"kszucs\",\n+ \"node_id\": \"MDQ6VXNlcjk2MTc0Nw==\",\n+ \"organizations_url\": \"https://api.github.com/users/kszucs/orgs\",\n+ \"received_events_url\": \"https://api.github.com/users/kszucs/received_events\",\n+ \"repos_url\": \"https://api.github.com/users/kszucs/repos\",\n+ \"site_admin\": false,\n+ \"starred_url\": \"https://api.github.com/users/kszucs/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/kszucs/subscriptions\",\n+ \"type\": \"User\",\n+ \"url\": \"https://api.github.com/users/kszucs\"\n+ }\n+ },\n+ \"issue\": {\n+ \"assignee\": null,\n+ \"assignees\": [],\n+ \"author_association\": \"MEMBER\",\n+ \"body\": \"\",\n+ \"closed_at\": null,\n+ \"comments\": 3,\n+ \"comments_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26/comments\",\n+ \"created_at\": \"2019-04-05T11:22:15Z\",\n+ \"events_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26/events\",\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot/pull/26\",\n+ \"id\": 429706959,\n+ \"labels\": [],\n+ \"labels_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26/labels{/name}\",\n+ \"locked\": false,\n+ \"milestone\": null,\n+ \"node_id\": \"MDExOlB1bGxSZXF1ZXN0MjY3Nzg1NTUy\",\n+ \"number\": 26,\n+ \"pull_request\": {\n+ \"diff_url\": \"https://github.com/ursa-labs/ursabot/pull/26.diff\",\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot/pull/26\",\n+ \"patch_url\": \"https://github.com/ursa-labs/ursabot/pull/26.patch\",\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot/pulls/26\"\n+ },\n+ \"repository_url\": \"https://api.github.com/repos/ursa-labs/ursabot\",\n+ \"state\": \"open\",\n+ \"title\": \"Unittests for GithubHook\",\n+ \"updated_at\": \"2019-04-05T11:55:43Z\",\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26\",\n+ \"user\": {\n+ \"avatar_url\": \"https://avatars1.githubusercontent.com/u/961747?v=4\",\n+ \"events_url\": \"https://api.github.com/users/kszucs/events{/privacy}\",\n+ \"followers_url\": \"https://api.github.com/users/kszucs/followers\",\n+ \"following_url\": \"https://api.github.com/users/kszucs/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/kszucs/gists{/gist_id}\",\n+ \"gravatar_id\": \"\",\n+ \"html_url\": \"https://github.com/kszucs\",\n+ \"id\": 961747,\n+ \"login\": \"kszucs\",\n+ \"node_id\": \"MDQ6VXNlcjk2MTc0Nw==\",\n+ \"organizations_url\": \"https://api.github.com/users/kszucs/orgs\",\n+ \"received_events_url\": \"https://api.github.com/users/kszucs/received_events\",\n+ \"repos_url\": \"https://api.github.com/users/kszucs/repos\",\n+ \"site_admin\": false,\n+ \"starred_url\": \"https://api.github.com/users/kszucs/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/kszucs/subscriptions\",\n+ \"type\": \"User\",\n+ \"url\": \"https://api.github.com/users/kszucs\"\n+ }\n+ },\n+ \"organization\": {\n+ \"avatar_url\": \"https://avatars2.githubusercontent.com/u/46514972?v=4\",\n+ \"description\": \"Innovation lab for open source data science tools, powered by Apache Arrow\",\n+ \"events_url\": \"https://api.github.com/orgs/ursa-labs/events\",\n+ \"hooks_url\": \"https://api.github.com/orgs/ursa-labs/hooks\",\n+ \"id\": 46514972,\n+ \"issues_url\": \"https://api.github.com/orgs/ursa-labs/issues\",\n+ \"login\": \"ursa-labs\",\n+ \"members_url\": \"https://api.github.com/orgs/ursa-labs/members{/member}\",\n+ \"node_id\": \"MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy\",\n+ \"public_members_url\": \"https://api.github.com/orgs/ursa-labs/public_members{/member}\",\n+ \"repos_url\": \"https://api.github.com/orgs/ursa-labs/repos\",\n+ \"url\": \"https://api.github.com/orgs/ursa-labs\"\n+ },\n+ \"repository\": {\n+ \"archive_url\": \"https://api.github.com/repos/ursa-labs/ursabot/{archive_format}{/ref}\",\n+ \"archived\": false,\n+ \"assignees_url\": \"https://api.github.com/repos/ursa-labs/ursabot/assignees{/user}\",\n+ \"blobs_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/blobs{/sha}\",\n+ \"branches_url\": \"https://api.github.com/repos/ursa-labs/ursabot/branches{/branch}\",\n+ \"clone_url\": \"https://github.com/ursa-labs/ursabot.git\",\n+ \"collaborators_url\": \"https://api.github.com/repos/ursa-labs/ursabot/collaborators{/collaborator}\",\n+ \"comments_url\": \"https://api.github.com/repos/ursa-labs/ursabot/comments{/number}\",\n+ \"commits_url\": \"https://api.github.com/repos/ursa-labs/ursabot/commits{/sha}\",\n+ \"compare_url\": \"https://api.github.com/repos/ursa-labs/ursabot/compare/{base}...{head}\",\n+ \"contents_url\": \"https://api.github.com/repos/ursa-labs/ursabot/contents/{+path}\",\n+ \"contributors_url\": \"https://api.github.com/repos/ursa-labs/ursabot/contributors\",\n+ \"created_at\": \"2019-02-04T15:40:31Z\",\n+ \"default_branch\": \"master\",\n+ \"deployments_url\": \"https://api.github.com/repos/ursa-labs/ursabot/deployments\",\n+ \"description\": null,\n+ \"disabled\": false,\n+ \"downloads_url\": \"https://api.github.com/repos/ursa-labs/ursabot/downloads\",\n+ \"events_url\": \"https://api.github.com/repos/ursa-labs/ursabot/events\",\n+ \"fork\": false,\n+ \"forks\": 0,\n+ \"forks_count\": 0,\n+ \"forks_url\": \"https://api.github.com/repos/ursa-labs/ursabot/forks\",\n+ \"full_name\": \"ursa-labs/ursabot\",\n+ \"git_commits_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/commits{/sha}\",\n+ \"git_refs_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/refs{/sha}\",\n+ \"git_tags_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/tags{/sha}\",\n+ \"git_url\": \"git://github.com/ursa-labs/ursabot.git\",\n+ \"has_downloads\": true,\n+ \"has_issues\": true,\n+ \"has_pages\": false,\n+ \"has_projects\": true,\n+ \"has_wiki\": true,\n+ \"homepage\": null,\n+ \"hooks_url\": \"https://api.github.com/repos/ursa-labs/ursabot/hooks\",\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot\",\n+ \"id\": 169101701,\n+ \"issue_comment_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/comments{/number}\",\n+ \"issue_events_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/events{/number}\",\n+ \"issues_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues{/number}\",\n+ \"keys_url\": \"https://api.github.com/repos/ursa-labs/ursabot/keys{/key_id}\",\n+ \"labels_url\": \"https://api.github.com/repos/ursa-labs/ursabot/labels{/name}\",\n+ \"language\": \"Jupyter Notebook\",\n+ \"languages_url\": \"https://api.github.com/repos/ursa-labs/ursabot/languages\",\n+ \"license\": null,\n+ \"merges_url\": \"https://api.github.com/repos/ursa-labs/ursabot/merges\",\n+ \"milestones_url\": \"https://api.github.com/repos/ursa-labs/ursabot/milestones{/number}\",\n+ \"mirror_url\": null,\n+ \"name\": \"ursabot\",\n+ \"node_id\": \"MDEwOlJlcG9zaXRvcnkxNjkxMDE3MDE=\",\n+ \"notifications_url\": \"https://api.github.com/repos/ursa-labs/ursabot/notifications{?since,all,participating}\",\n+ \"open_issues\": 19,\n+ \"open_issues_count\": 19,\n+ \"owner\": {\n+ \"avatar_url\": \"https://avatars2.githubusercontent.com/u/46514972?v=4\",\n+ \"events_url\": \"https://api.github.com/users/ursa-labs/events{/privacy}\",\n+ \"followers_url\": \"https://api.github.com/users/ursa-labs/followers\",\n+ \"following_url\": \"https://api.github.com/users/ursa-labs/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/ursa-labs/gists{/gist_id}\",\n+ \"gravatar_id\": \"\",\n+ \"html_url\": \"https://github.com/ursa-labs\",\n+ \"id\": 46514972,\n+ \"login\": \"ursa-labs\",\n+ \"node_id\": \"MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy\",\n+ \"organizations_url\": \"https://api.github.com/users/ursa-labs/orgs\",\n+ \"received_events_url\": \"https://api.github.com/users/ursa-labs/received_events\",\n+ \"repos_url\": \"https://api.github.com/users/ursa-labs/repos\",\n+ \"site_admin\": false,\n+ \"starred_url\": \"https://api.github.com/users/ursa-labs/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/ursa-labs/subscriptions\",\n+ \"type\": \"Organization\",\n+ \"url\": \"https://api.github.com/users/ursa-labs\"\n+ },\n+ \"private\": false,\n+ \"pulls_url\": \"https://api.github.com/repos/ursa-labs/ursabot/pulls{/number}\",\n+ \"pushed_at\": \"2019-04-05T11:22:16Z\",\n+ \"releases_url\": \"https://api.github.com/repos/ursa-labs/ursabot/releases{/id}\",\n+ \"size\": 892,\n+ \"ssh_url\": \"git@github.com:ursa-labs/ursabot.git\",\n+ \"stargazers_count\": 1,\n+ \"stargazers_url\": \"https://api.github.com/repos/ursa-labs/ursabot/stargazers\",\n+ \"statuses_url\": \"https://api.github.com/repos/ursa-labs/ursabot/statuses/{sha}\",\n+ \"subscribers_url\": \"https://api.github.com/repos/ursa-labs/ursabot/subscribers\",\n+ \"subscription_url\": \"https://api.github.com/repos/ursa-labs/ursabot/subscription\",\n+ \"svn_url\": \"https://github.com/ursa-labs/ursabot\",\n+ \"tags_url\": \"https://api.github.com/repos/ursa-labs/ursabot/tags\",\n+ \"teams_url\": \"https://api.github.com/repos/ursa-labs/ursabot/teams\",\n+ \"trees_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/trees{/sha}\",\n+ \"updated_at\": \"2019-04-04T17:49:10Z\",\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot\",\n+ \"watchers\": 1,\n+ \"watchers_count\": 1\n+ },\n+ \"sender\": {\n+ \"avatar_url\": \"https://avatars1.githubusercontent.com/u/961747?v=4\",\n+ \"events_url\": \"https://api.github.com/users/kszucs/events{/privacy}\",\n+ \"followers_url\": \"https://api.github.com/users/kszucs/followers\",\n+ \"following_url\": \"https://api.github.com/users/kszucs/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/kszucs/gists{/gist_id}\",\n+ \"gravatar_id\": \"\",\n+ \"html_url\": \"https://github.com/kszucs\",\n+ \"id\": 961747,\n+ \"login\": \"kszucs\",\n+ \"node_id\": \"MDQ6VXNlcjk2MTc0Nw==\",\n+ \"organizations_url\": \"https://api.github.com/users/kszucs/orgs\",\n+ \"received_events_url\": \"https://api.github.com/users/kszucs/received_events\",\n+ \"repos_url\": \"https://api.github.com/users/kszucs/repos\",\n+ \"site_admin\": false,\n+ \"starred_url\": \"https://api.github.com/users/kszucs/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/kszucs/subscriptions\",\n+ \"type\": \"User\",\n+ \"url\": \"https://api.github.com/users/kszucs\"\n+ }\n+}"
- },
- {
- "sha": "7ef554e333327f0e62aa1fd76b4b17844a39adeb",
- "filename": "ursabot/tests/fixtures/issue-comment-by-ursabot.json",
- "status": "added",
- "additions": 212,
- "deletions": 0,
- "changes": 212,
- "blob_url": "https://github.com/ursa-labs/ursabot/blob/70267dee34884e4b972388e1b30d57f6248c58d0/ursabot/tests/fixtures/issue-comment-by-ursabot.json",
- "raw_url": "https://github.com/ursa-labs/ursabot/raw/70267dee34884e4b972388e1b30d57f6248c58d0/ursabot/tests/fixtures/issue-comment-by-ursabot.json",
- "contents_url": "https://api.github.com/repos/ursa-labs/ursabot/contents/ursabot/tests/fixtures/issue-comment-by-ursabot.json?ref=70267dee34884e4b972388e1b30d57f6248c58d0",
- "patch": "@@ -0,0 +1,212 @@\n+{\n+ \"action\": \"created\",\n+ \"comment\": {\n+ \"author_association\": \"NONE\",\n+ \"body\": \"Unknown command \\\"\\\"\",\n+ \"created_at\": \"2019-04-05T11:35:47Z\",\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot/pull/26#issuecomment-480243815\",\n+ \"id\": 480243815,\n+ \"issue_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26\",\n+ \"node_id\": \"MDEyOklzc3VlQ29tbWVudDQ4MDI0MzgxNQ==\",\n+ \"updated_at\": \"2019-04-05T11:35:47Z\",\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/comments/480243815\",\n+ \"user\": {\n+ \"avatar_url\": \"https://avatars2.githubusercontent.com/u/49275095?v=4\",\n+ \"events_url\": \"https://api.github.com/users/ursabot/events{/privacy}\",\n+ \"followers_url\": \"https://api.github.com/users/ursabot/followers\",\n+ \"following_url\": \"https://api.github.com/users/ursabot/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/ursabot/gists{/gist_id}\",\n+ \"gravatar_id\": \"\",\n+ \"html_url\": \"https://github.com/ursabot\",\n+ \"id\": 49275095,\n+ \"login\": \"ursabot\",\n+ \"node_id\": \"MDQ6VXNlcjQ5Mjc1MDk1\",\n+ \"organizations_url\": \"https://api.github.com/users/ursabot/orgs\",\n+ \"received_events_url\": \"https://api.github.com/users/ursabot/received_events\",\n+ \"repos_url\": \"https://api.github.com/users/ursabot/repos\",\n+ \"site_admin\": false,\n+ \"starred_url\": \"https://api.github.com/users/ursabot/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/ursabot/subscriptions\",\n+ \"type\": \"User\",\n+ \"url\": \"https://api.github.com/users/ursabot\"\n+ }\n+ },\n+ \"issue\": {\n+ \"assignee\": null,\n+ \"assignees\": [],\n+ \"author_association\": \"MEMBER\",\n+ \"body\": \"\",\n+ \"closed_at\": null,\n+ \"comments\": 2,\n+ \"comments_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26/comments\",\n+ \"created_at\": \"2019-04-05T11:22:15Z\",\n+ \"events_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26/events\",\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot/pull/26\",\n+ \"id\": 429706959,\n+ \"labels\": [],\n+ \"labels_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26/labels{/name}\",\n+ \"locked\": false,\n+ \"milestone\": null,\n+ \"node_id\": \"MDExOlB1bGxSZXF1ZXN0MjY3Nzg1NTUy\",\n+ \"number\": 26,\n+ \"pull_request\": {\n+ \"diff_url\": \"https://github.com/ursa-labs/ursabot/pull/26.diff\",\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot/pull/26\",\n+ \"patch_url\": \"https://github.com/ursa-labs/ursabot/pull/26.patch\",\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot/pulls/26\"\n+ },\n+ \"repository_url\": \"https://api.github.com/repos/ursa-labs/ursabot\",\n+ \"state\": \"open\",\n+ \"title\": \"Unittests for GithubHook\",\n+ \"updated_at\": \"2019-04-05T11:35:47Z\",\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26\",\n+ \"user\": {\n+ \"avatar_url\": \"https://avatars1.githubusercontent.com/u/961747?v=4\",\n+ \"events_url\": \"https://api.github.com/users/kszucs/events{/privacy}\",\n+ \"followers_url\": \"https://api.github.com/users/kszucs/followers\",\n+ \"following_url\": \"https://api.github.com/users/kszucs/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/kszucs/gists{/gist_id}\",\n+ \"gravatar_id\": \"\",\n+ \"html_url\": \"https://github.com/kszucs\",\n+ \"id\": 961747,\n+ \"login\": \"kszucs\",\n+ \"node_id\": \"MDQ6VXNlcjk2MTc0Nw==\",\n+ \"organizations_url\": \"https://api.github.com/users/kszucs/orgs\",\n+ \"received_events_url\": \"https://api.github.com/users/kszucs/received_events\",\n+ \"repos_url\": \"https://api.github.com/users/kszucs/repos\",\n+ \"site_admin\": false,\n+ \"starred_url\": \"https://api.github.com/users/kszucs/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/kszucs/subscriptions\",\n+ \"type\": \"User\",\n+ \"url\": \"https://api.github.com/users/kszucs\"\n+ }\n+ },\n+ \"organization\": {\n+ \"avatar_url\": \"https://avatars2.githubusercontent.com/u/46514972?v=4\",\n+ \"description\": \"Innovation lab for open source data science tools, powered by Apache Arrow\",\n+ \"events_url\": \"https://api.github.com/orgs/ursa-labs/events\",\n+ \"hooks_url\": \"https://api.github.com/orgs/ursa-labs/hooks\",\n+ \"id\": 46514972,\n+ \"issues_url\": \"https://api.github.com/orgs/ursa-labs/issues\",\n+ \"login\": \"ursa-labs\",\n+ \"members_url\": \"https://api.github.com/orgs/ursa-labs/members{/member}\",\n+ \"node_id\": \"MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy\",\n+ \"public_members_url\": \"https://api.github.com/orgs/ursa-labs/public_members{/member}\",\n+ \"repos_url\": \"https://api.github.com/orgs/ursa-labs/repos\",\n+ \"url\": \"https://api.github.com/orgs/ursa-labs\"\n+ },\n+ \"repository\": {\n+ \"archive_url\": \"https://api.github.com/repos/ursa-labs/ursabot/{archive_format}{/ref}\",\n+ \"archived\": false,\n+ \"assignees_url\": \"https://api.github.com/repos/ursa-labs/ursabot/assignees{/user}\",\n+ \"blobs_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/blobs{/sha}\",\n+ \"branches_url\": \"https://api.github.com/repos/ursa-labs/ursabot/branches{/branch}\",\n+ \"clone_url\": \"https://github.com/ursa-labs/ursabot.git\",\n+ \"collaborators_url\": \"https://api.github.com/repos/ursa-labs/ursabot/collaborators{/collaborator}\",\n+ \"comments_url\": \"https://api.github.com/repos/ursa-labs/ursabot/comments{/number}\",\n+ \"commits_url\": \"https://api.github.com/repos/ursa-labs/ursabot/commits{/sha}\",\n+ \"compare_url\": \"https://api.github.com/repos/ursa-labs/ursabot/compare/{base}...{head}\",\n+ \"contents_url\": \"https://api.github.com/repos/ursa-labs/ursabot/contents/{+path}\",\n+ \"contributors_url\": \"https://api.github.com/repos/ursa-labs/ursabot/contributors\",\n+ \"created_at\": \"2019-02-04T15:40:31Z\",\n+ \"default_branch\": \"master\",\n+ \"deployments_url\": \"https://api.github.com/repos/ursa-labs/ursabot/deployments\",\n+ \"description\": null,\n+ \"disabled\": false,\n+ \"downloads_url\": \"https://api.github.com/repos/ursa-labs/ursabot/downloads\",\n+ \"events_url\": \"https://api.github.com/repos/ursa-labs/ursabot/events\",\n+ \"fork\": false,\n+ \"forks\": 0,\n+ \"forks_count\": 0,\n+ \"forks_url\": \"https://api.github.com/repos/ursa-labs/ursabot/forks\",\n+ \"full_name\": \"ursa-labs/ursabot\",\n+ \"git_commits_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/commits{/sha}\",\n+ \"git_refs_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/refs{/sha}\",\n+ \"git_tags_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/tags{/sha}\",\n+ \"git_url\": \"git://github.com/ursa-labs/ursabot.git\",\n+ \"has_downloads\": true,\n+ \"has_issues\": true,\n+ \"has_pages\": false,\n+ \"has_projects\": true,\n+ \"has_wiki\": true,\n+ \"homepage\": null,\n+ \"hooks_url\": \"https://api.github.com/repos/ursa-labs/ursabot/hooks\",\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot\",\n+ \"id\": 169101701,\n+ \"issue_comment_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/comments{/number}\",\n+ \"issue_events_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/events{/number}\",\n+ \"issues_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues{/number}\",\n+ \"keys_url\": \"https://api.github.com/repos/ursa-labs/ursabot/keys{/key_id}\",\n+ \"labels_url\": \"https://api.github.com/repos/ursa-labs/ursabot/labels{/name}\",\n+ \"language\": \"Jupyter Notebook\",\n+ \"languages_url\": \"https://api.github.com/repos/ursa-labs/ursabot/languages\",\n+ \"license\": null,\n+ \"merges_url\": \"https://api.github.com/repos/ursa-labs/ursabot/merges\",\n+ \"milestones_url\": \"https://api.github.com/repos/ursa-labs/ursabot/milestones{/number}\",\n+ \"mirror_url\": null,\n+ \"name\": \"ursabot\",\n+ \"node_id\": \"MDEwOlJlcG9zaXRvcnkxNjkxMDE3MDE=\",\n+ \"notifications_url\": \"https://api.github.com/repos/ursa-labs/ursabot/notifications{?since,all,participating}\",\n+ \"open_issues\": 19,\n+ \"open_issues_count\": 19,\n+ \"owner\": {\n+ \"avatar_url\": \"https://avatars2.githubusercontent.com/u/46514972?v=4\",\n+ \"events_url\": \"https://api.github.com/users/ursa-labs/events{/privacy}\",\n+ \"followers_url\": \"https://api.github.com/users/ursa-labs/followers\",\n+ \"following_url\": \"https://api.github.com/users/ursa-labs/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/ursa-labs/gists{/gist_id}\",\n+ \"gravatar_id\": \"\",\n+ \"html_url\": \"https://github.com/ursa-labs\",\n+ \"id\": 46514972,\n+ \"login\": \"ursa-labs\",\n+ \"node_id\": \"MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy\",\n+ \"organizations_url\": \"https://api.github.com/users/ursa-labs/orgs\",\n+ \"received_events_url\": \"https://api.github.com/users/ursa-labs/received_events\",\n+ \"repos_url\": \"https://api.github.com/users/ursa-labs/repos\",\n+ \"site_admin\": false,\n+ \"starred_url\": \"https://api.github.com/users/ursa-labs/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/ursa-labs/subscriptions\",\n+ \"type\": \"Organization\",\n+ \"url\": \"https://api.github.com/users/ursa-labs\"\n+ },\n+ \"private\": false,\n+ \"pulls_url\": \"https://api.github.com/repos/ursa-labs/ursabot/pulls{/number}\",\n+ \"pushed_at\": \"2019-04-05T11:22:16Z\",\n+ \"releases_url\": \"https://api.github.com/repos/ursa-labs/ursabot/releases{/id}\",\n+ \"size\": 892,\n+ \"ssh_url\": \"git@github.com:ursa-labs/ursabot.git\",\n+ \"stargazers_count\": 1,\n+ \"stargazers_url\": \"https://api.github.com/repos/ursa-labs/ursabot/stargazers\",\n+ \"statuses_url\": \"https://api.github.com/repos/ursa-labs/ursabot/statuses/{sha}\",\n+ \"subscribers_url\": \"https://api.github.com/repos/ursa-labs/ursabot/subscribers\",\n+ \"subscription_url\": \"https://api.github.com/repos/ursa-labs/ursabot/subscription\",\n+ \"svn_url\": \"https://github.com/ursa-labs/ursabot\",\n+ \"tags_url\": \"https://api.github.com/repos/ursa-labs/ursabot/tags\",\n+ \"teams_url\": \"https://api.github.com/repos/ursa-labs/ursabot/teams\",\n+ \"trees_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/trees{/sha}\",\n+ \"updated_at\": \"2019-04-04T17:49:10Z\",\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot\",\n+ \"watchers\": 1,\n+ \"watchers_count\": 1\n+ },\n+ \"sender\": {\n+ \"avatar_url\": \"https://avatars2.githubusercontent.com/u/49275095?v=4\",\n+ \"events_url\": \"https://api.github.com/users/ursabot/events{/privacy}\",\n+ \"followers_url\": \"https://api.github.com/users/ursabot/followers\",\n+ \"following_url\": \"https://api.github.com/users/ursabot/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/ursabot/gists{/gist_id}\",\n+ \"gravatar_id\": \"\",\n+ \"html_url\": \"https://github.com/ursabot\",\n+ \"id\": 49275095,\n+ \"login\": \"ursabot\",\n+ \"node_id\": \"MDQ6VXNlcjQ5Mjc1MDk1\",\n+ \"organizations_url\": \"https://api.github.com/users/ursabot/orgs\",\n+ \"received_events_url\": \"https://api.github.com/users/ursabot/received_events\",\n+ \"repos_url\": \"https://api.github.com/users/ursabot/repos\",\n+ \"site_admin\": false,\n+ \"starred_url\": \"https://api.github.com/users/ursabot/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/ursabot/subscriptions\",\n+ \"type\": \"User\",\n+ \"url\": \"https://api.github.com/users/ursabot\"\n+ }\n+}"
- },
- {
- "sha": "a8082dbc91fdfe815b795e49ec10e49000771ef5",
- "filename": "ursabot/tests/fixtures/issue-comment-not-mentioning-ursabot.json",
- "status": "added",
- "additions": 212,
- "deletions": 0,
- "changes": 212,
- "blob_url": "https://github.com/ursa-labs/ursabot/blob/70267dee34884e4b972388e1b30d57f6248c58d0/ursabot/tests/fixtures/issue-comment-not-mentioning-ursabot.json",
- "raw_url": "https://github.com/ursa-labs/ursabot/raw/70267dee34884e4b972388e1b30d57f6248c58d0/ursabot/tests/fixtures/issue-comment-not-mentioning-ursabot.json",
- "contents_url": "https://api.github.com/repos/ursa-labs/ursabot/contents/ursabot/tests/fixtures/issue-comment-not-mentioning-ursabot.json?ref=70267dee34884e4b972388e1b30d57f6248c58d0",
- "patch": "@@ -0,0 +1,212 @@\n+{\n+ \"action\": \"created\",\n+ \"comment\": {\n+ \"author_association\": \"MEMBER\",\n+ \"body\": \"bear is no game\",\n+ \"created_at\": \"2019-04-05T11:26:56Z\",\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot/pull/26#issuecomment-480241727\",\n+ \"id\": 480241727,\n+ \"issue_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26\",\n+ \"node_id\": \"MDEyOklzc3VlQ29tbWVudDQ4MDI0MTcyNw==\",\n+ \"updated_at\": \"2019-04-05T11:26:56Z\",\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/comments/480241727\",\n+ \"user\": {\n+ \"avatar_url\": \"https://avatars1.githubusercontent.com/u/961747?v=4\",\n+ \"events_url\": \"https://api.github.com/users/kszucs/events{/privacy}\",\n+ \"followers_url\": \"https://api.github.com/users/kszucs/followers\",\n+ \"following_url\": \"https://api.github.com/users/kszucs/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/kszucs/gists{/gist_id}\",\n+ \"gravatar_id\": \"\",\n+ \"html_url\": \"https://github.com/kszucs\",\n+ \"id\": 961747,\n+ \"login\": \"kszucs\",\n+ \"node_id\": \"MDQ6VXNlcjk2MTc0Nw==\",\n+ \"organizations_url\": \"https://api.github.com/users/kszucs/orgs\",\n+ \"received_events_url\": \"https://api.github.com/users/kszucs/received_events\",\n+ \"repos_url\": \"https://api.github.com/users/kszucs/repos\",\n+ \"site_admin\": false,\n+ \"starred_url\": \"https://api.github.com/users/kszucs/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/kszucs/subscriptions\",\n+ \"type\": \"User\",\n+ \"url\": \"https://api.github.com/users/kszucs\"\n+ }\n+ },\n+ \"issue\": {\n+ \"assignee\": null,\n+ \"assignees\": [],\n+ \"author_association\": \"MEMBER\",\n+ \"body\": \"\",\n+ \"closed_at\": null,\n+ \"comments\": 0,\n+ \"comments_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26/comments\",\n+ \"created_at\": \"2019-04-05T11:22:15Z\",\n+ \"events_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26/events\",\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot/pull/26\",\n+ \"id\": 429706959,\n+ \"labels\": [],\n+ \"labels_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26/labels{/name}\",\n+ \"locked\": false,\n+ \"milestone\": null,\n+ \"node_id\": \"MDExOlB1bGxSZXF1ZXN0MjY3Nzg1NTUy\",\n+ \"number\": 26,\n+ \"pull_request\": {\n+ \"diff_url\": \"https://github.com/ursa-labs/ursabot/pull/26.diff\",\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot/pull/26\",\n+ \"patch_url\": \"https://github.com/ursa-labs/ursabot/pull/26.patch\",\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot/pulls/26\"\n+ },\n+ \"repository_url\": \"https://api.github.com/repos/ursa-labs/ursabot\",\n+ \"state\": \"open\",\n+ \"title\": \"Unittests for GithubHook\",\n+ \"updated_at\": \"2019-04-05T11:26:56Z\",\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26\",\n+ \"user\": {\n+ \"avatar_url\": \"https://avatars1.githubusercontent.com/u/961747?v=4\",\n+ \"events_url\": \"https://api.github.com/users/kszucs/events{/privacy}\",\n+ \"followers_url\": \"https://api.github.com/users/kszucs/followers\",\n+ \"following_url\": \"https://api.github.com/users/kszucs/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/kszucs/gists{/gist_id}\",\n+ \"gravatar_id\": \"\",\n+ \"html_url\": \"https://github.com/kszucs\",\n+ \"id\": 961747,\n+ \"login\": \"kszucs\",\n+ \"node_id\": \"MDQ6VXNlcjk2MTc0Nw==\",\n+ \"organizations_url\": \"https://api.github.com/users/kszucs/orgs\",\n+ \"received_events_url\": \"https://api.github.com/users/kszucs/received_events\",\n+ \"repos_url\": \"https://api.github.com/users/kszucs/repos\",\n+ \"site_admin\": false,\n+ \"starred_url\": \"https://api.github.com/users/kszucs/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/kszucs/subscriptions\",\n+ \"type\": \"User\",\n+ \"url\": \"https://api.github.com/users/kszucs\"\n+ }\n+ },\n+ \"organization\": {\n+ \"avatar_url\": \"https://avatars2.githubusercontent.com/u/46514972?v=4\",\n+ \"description\": \"Innovation lab for open source data science tools, powered by Apache Arrow\",\n+ \"events_url\": \"https://api.github.com/orgs/ursa-labs/events\",\n+ \"hooks_url\": \"https://api.github.com/orgs/ursa-labs/hooks\",\n+ \"id\": 46514972,\n+ \"issues_url\": \"https://api.github.com/orgs/ursa-labs/issues\",\n+ \"login\": \"ursa-labs\",\n+ \"members_url\": \"https://api.github.com/orgs/ursa-labs/members{/member}\",\n+ \"node_id\": \"MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy\",\n+ \"public_members_url\": \"https://api.github.com/orgs/ursa-labs/public_members{/member}\",\n+ \"repos_url\": \"https://api.github.com/orgs/ursa-labs/repos\",\n+ \"url\": \"https://api.github.com/orgs/ursa-labs\"\n+ },\n+ \"repository\": {\n+ \"archive_url\": \"https://api.github.com/repos/ursa-labs/ursabot/{archive_format}{/ref}\",\n+ \"archived\": false,\n+ \"assignees_url\": \"https://api.github.com/repos/ursa-labs/ursabot/assignees{/user}\",\n+ \"blobs_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/blobs{/sha}\",\n+ \"branches_url\": \"https://api.github.com/repos/ursa-labs/ursabot/branches{/branch}\",\n+ \"clone_url\": \"https://github.com/ursa-labs/ursabot.git\",\n+ \"collaborators_url\": \"https://api.github.com/repos/ursa-labs/ursabot/collaborators{/collaborator}\",\n+ \"comments_url\": \"https://api.github.com/repos/ursa-labs/ursabot/comments{/number}\",\n+ \"commits_url\": \"https://api.github.com/repos/ursa-labs/ursabot/commits{/sha}\",\n+ \"compare_url\": \"https://api.github.com/repos/ursa-labs/ursabot/compare/{base}...{head}\",\n+ \"contents_url\": \"https://api.github.com/repos/ursa-labs/ursabot/contents/{+path}\",\n+ \"contributors_url\": \"https://api.github.com/repos/ursa-labs/ursabot/contributors\",\n+ \"created_at\": \"2019-02-04T15:40:31Z\",\n+ \"default_branch\": \"master\",\n+ \"deployments_url\": \"https://api.github.com/repos/ursa-labs/ursabot/deployments\",\n+ \"description\": null,\n+ \"disabled\": false,\n+ \"downloads_url\": \"https://api.github.com/repos/ursa-labs/ursabot/downloads\",\n+ \"events_url\": \"https://api.github.com/repos/ursa-labs/ursabot/events\",\n+ \"fork\": false,\n+ \"forks\": 0,\n+ \"forks_count\": 0,\n+ \"forks_url\": \"https://api.github.com/repos/ursa-labs/ursabot/forks\",\n+ \"full_name\": \"ursa-labs/ursabot\",\n+ \"git_commits_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/commits{/sha}\",\n+ \"git_refs_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/refs{/sha}\",\n+ \"git_tags_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/tags{/sha}\",\n+ \"git_url\": \"git://github.com/ursa-labs/ursabot.git\",\n+ \"has_downloads\": true,\n+ \"has_issues\": true,\n+ \"has_pages\": false,\n+ \"has_projects\": true,\n+ \"has_wiki\": true,\n+ \"homepage\": null,\n+ \"hooks_url\": \"https://api.github.com/repos/ursa-labs/ursabot/hooks\",\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot\",\n+ \"id\": 169101701,\n+ \"issue_comment_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/comments{/number}\",\n+ \"issue_events_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/events{/number}\",\n+ \"issues_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues{/number}\",\n+ \"keys_url\": \"https://api.github.com/repos/ursa-labs/ursabot/keys{/key_id}\",\n+ \"labels_url\": \"https://api.github.com/repos/ursa-labs/ursabot/labels{/name}\",\n+ \"language\": \"Jupyter Notebook\",\n+ \"languages_url\": \"https://api.github.com/repos/ursa-labs/ursabot/languages\",\n+ \"license\": null,\n+ \"merges_url\": \"https://api.github.com/repos/ursa-labs/ursabot/merges\",\n+ \"milestones_url\": \"https://api.github.com/repos/ursa-labs/ursabot/milestones{/number}\",\n+ \"mirror_url\": null,\n+ \"name\": \"ursabot\",\n+ \"node_id\": \"MDEwOlJlcG9zaXRvcnkxNjkxMDE3MDE=\",\n+ \"notifications_url\": \"https://api.github.com/repos/ursa-labs/ursabot/notifications{?since,all,participating}\",\n+ \"open_issues\": 19,\n+ \"open_issues_count\": 19,\n+ \"owner\": {\n+ \"avatar_url\": \"https://avatars2.githubusercontent.com/u/46514972?v=4\",\n+ \"events_url\": \"https://api.github.com/users/ursa-labs/events{/privacy}\",\n+ \"followers_url\": \"https://api.github.com/users/ursa-labs/followers\",\n+ \"following_url\": \"https://api.github.com/users/ursa-labs/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/ursa-labs/gists{/gist_id}\",\n+ \"gravatar_id\": \"\",\n+ \"html_url\": \"https://github.com/ursa-labs\",\n+ \"id\": 46514972,\n+ \"login\": \"ursa-labs\",\n+ \"node_id\": \"MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy\",\n+ \"organizations_url\": \"https://api.github.com/users/ursa-labs/orgs\",\n+ \"received_events_url\": \"https://api.github.com/users/ursa-labs/received_events\",\n+ \"repos_url\": \"https://api.github.com/users/ursa-labs/repos\",\n+ \"site_admin\": false,\n+ \"starred_url\": \"https://api.github.com/users/ursa-labs/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/ursa-labs/subscriptions\",\n+ \"type\": \"Organization\",\n+ \"url\": \"https://api.github.com/users/ursa-labs\"\n+ },\n+ \"private\": false,\n+ \"pulls_url\": \"https://api.github.com/repos/ursa-labs/ursabot/pulls{/number}\",\n+ \"pushed_at\": \"2019-04-05T11:22:16Z\",\n+ \"releases_url\": \"https://api.github.com/repos/ursa-labs/ursabot/releases{/id}\",\n+ \"size\": 892,\n+ \"ssh_url\": \"git@github.com:ursa-labs/ursabot.git\",\n+ \"stargazers_count\": 1,\n+ \"stargazers_url\": \"https://api.github.com/repos/ursa-labs/ursabot/stargazers\",\n+ \"statuses_url\": \"https://api.github.com/repos/ursa-labs/ursabot/statuses/{sha}\",\n+ \"subscribers_url\": \"https://api.github.com/repos/ursa-labs/ursabot/subscribers\",\n+ \"subscription_url\": \"https://api.github.com/repos/ursa-labs/ursabot/subscription\",\n+ \"svn_url\": \"https://github.com/ursa-labs/ursabot\",\n+ \"tags_url\": \"https://api.github.com/repos/ursa-labs/ursabot/tags\",\n+ \"teams_url\": \"https://api.github.com/repos/ursa-labs/ursabot/teams\",\n+ \"trees_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/trees{/sha}\",\n+ \"updated_at\": \"2019-04-04T17:49:10Z\",\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot\",\n+ \"watchers\": 1,\n+ \"watchers_count\": 1\n+ },\n+ \"sender\": {\n+ \"avatar_url\": \"https://avatars1.githubusercontent.com/u/961747?v=4\",\n+ \"events_url\": \"https://api.github.com/users/kszucs/events{/privacy}\",\n+ \"followers_url\": \"https://api.github.com/users/kszucs/followers\",\n+ \"following_url\": \"https://api.github.com/users/kszucs/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/kszucs/gists{/gist_id}\",\n+ \"gravatar_id\": \"\",\n+ \"html_url\": \"https://github.com/kszucs\",\n+ \"id\": 961747,\n+ \"login\": \"kszucs\",\n+ \"node_id\": \"MDQ6VXNlcjk2MTc0Nw==\",\n+ \"organizations_url\": \"https://api.github.com/users/kszucs/orgs\",\n+ \"received_events_url\": \"https://api.github.com/users/kszucs/received_events\",\n+ \"repos_url\": \"https://api.github.com/users/kszucs/repos\",\n+ \"site_admin\": false,\n+ \"starred_url\": \"https://api.github.com/users/kszucs/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/kszucs/subscriptions\",\n+ \"type\": \"User\",\n+ \"url\": \"https://api.github.com/users/kszucs\"\n+ }\n+}"
- },
- {
- "sha": "2770e29ba9086394455315e590c0b433d08e437e",
- "filename": "ursabot/tests/fixtures/issue-comment-with-empty-command.json",
- "status": "added",
- "additions": 212,
- "deletions": 0,
- "changes": 212,
- "blob_url": "https://github.com/ursa-labs/ursabot/blob/70267dee34884e4b972388e1b30d57f6248c58d0/ursabot/tests/fixtures/issue-comment-with-empty-command.json",
- "raw_url": "https://github.com/ursa-labs/ursabot/raw/70267dee34884e4b972388e1b30d57f6248c58d0/ursabot/tests/fixtures/issue-comment-with-empty-command.json",
- "contents_url": "https://api.github.com/repos/ursa-labs/ursabot/contents/ursabot/tests/fixtures/issue-comment-with-empty-command.json?ref=70267dee34884e4b972388e1b30d57f6248c58d0",
- "patch": "@@ -0,0 +1,212 @@\n+{\n+ \"action\": \"created\",\n+ \"comment\": {\n+ \"author_association\": \"MEMBER\",\n+ \"body\": \"@ursabot \",\n+ \"created_at\": \"2019-04-05T11:35:46Z\",\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot/pull/26#issuecomment-480243811\",\n+ \"id\": 480243811,\n+ \"issue_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26\",\n+ \"node_id\": \"MDEyOklzc3VlQ29tbWVudDQ4MDI0MzgxMQ==\",\n+ \"updated_at\": \"2019-04-05T11:35:46Z\",\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/comments/480243811\",\n+ \"user\": {\n+ \"avatar_url\": \"https://avatars1.githubusercontent.com/u/961747?v=4\",\n+ \"events_url\": \"https://api.github.com/users/kszucs/events{/privacy}\",\n+ \"followers_url\": \"https://api.github.com/users/kszucs/followers\",\n+ \"following_url\": \"https://api.github.com/users/kszucs/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/kszucs/gists{/gist_id}\",\n+ \"gravatar_id\": \"\",\n+ \"html_url\": \"https://github.com/kszucs\",\n+ \"id\": 961747,\n+ \"login\": \"kszucs\",\n+ \"node_id\": \"MDQ6VXNlcjk2MTc0Nw==\",\n+ \"organizations_url\": \"https://api.github.com/users/kszucs/orgs\",\n+ \"received_events_url\": \"https://api.github.com/users/kszucs/received_events\",\n+ \"repos_url\": \"https://api.github.com/users/kszucs/repos\",\n+ \"site_admin\": false,\n+ \"starred_url\": \"https://api.github.com/users/kszucs/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/kszucs/subscriptions\",\n+ \"type\": \"User\",\n+ \"url\": \"https://api.github.com/users/kszucs\"\n+ }\n+ },\n+ \"issue\": {\n+ \"assignee\": null,\n+ \"assignees\": [],\n+ \"author_association\": \"MEMBER\",\n+ \"body\": \"\",\n+ \"closed_at\": null,\n+ \"comments\": 1,\n+ \"comments_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26/comments\",\n+ \"created_at\": \"2019-04-05T11:22:15Z\",\n+ \"events_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26/events\",\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot/pull/26\",\n+ \"id\": 429706959,\n+ \"labels\": [],\n+ \"labels_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26/labels{/name}\",\n+ \"locked\": false,\n+ \"milestone\": null,\n+ \"node_id\": \"MDExOlB1bGxSZXF1ZXN0MjY3Nzg1NTUy\",\n+ \"number\": 26,\n+ \"pull_request\": {\n+ \"diff_url\": \"https://github.com/ursa-labs/ursabot/pull/26.diff\",\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot/pull/26\",\n+ \"patch_url\": \"https://github.com/ursa-labs/ursabot/pull/26.patch\",\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot/pulls/26\"\n+ },\n+ \"repository_url\": \"https://api.github.com/repos/ursa-labs/ursabot\",\n+ \"state\": \"open\",\n+ \"title\": \"Unittests for GithubHook\",\n+ \"updated_at\": \"2019-04-05T11:35:46Z\",\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26\",\n+ \"user\": {\n+ \"avatar_url\": \"https://avatars1.githubusercontent.com/u/961747?v=4\",\n+ \"events_url\": \"https://api.github.com/users/kszucs/events{/privacy}\",\n+ \"followers_url\": \"https://api.github.com/users/kszucs/followers\",\n+ \"following_url\": \"https://api.github.com/users/kszucs/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/kszucs/gists{/gist_id}\",\n+ \"gravatar_id\": \"\",\n+ \"html_url\": \"https://github.com/kszucs\",\n+ \"id\": 961747,\n+ \"login\": \"kszucs\",\n+ \"node_id\": \"MDQ6VXNlcjk2MTc0Nw==\",\n+ \"organizations_url\": \"https://api.github.com/users/kszucs/orgs\",\n+ \"received_events_url\": \"https://api.github.com/users/kszucs/received_events\",\n+ \"repos_url\": \"https://api.github.com/users/kszucs/repos\",\n+ \"site_admin\": false,\n+ \"starred_url\": \"https://api.github.com/users/kszucs/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/kszucs/subscriptions\",\n+ \"type\": \"User\",\n+ \"url\": \"https://api.github.com/users/kszucs\"\n+ }\n+ },\n+ \"organization\": {\n+ \"avatar_url\": \"https://avatars2.githubusercontent.com/u/46514972?v=4\",\n+ \"description\": \"Innovation lab for open source data science tools, powered by Apache Arrow\",\n+ \"events_url\": \"https://api.github.com/orgs/ursa-labs/events\",\n+ \"hooks_url\": \"https://api.github.com/orgs/ursa-labs/hooks\",\n+ \"id\": 46514972,\n+ \"issues_url\": \"https://api.github.com/orgs/ursa-labs/issues\",\n+ \"login\": \"ursa-labs\",\n+ \"members_url\": \"https://api.github.com/orgs/ursa-labs/members{/member}\",\n+ \"node_id\": \"MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy\",\n+ \"public_members_url\": \"https://api.github.com/orgs/ursa-labs/public_members{/member}\",\n+ \"repos_url\": \"https://api.github.com/orgs/ursa-labs/repos\",\n+ \"url\": \"https://api.github.com/orgs/ursa-labs\"\n+ },\n+ \"repository\": {\n+ \"archive_url\": \"https://api.github.com/repos/ursa-labs/ursabot/{archive_format}{/ref}\",\n+ \"archived\": false,\n+ \"assignees_url\": \"https://api.github.com/repos/ursa-labs/ursabot/assignees{/user}\",\n+ \"blobs_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/blobs{/sha}\",\n+ \"branches_url\": \"https://api.github.com/repos/ursa-labs/ursabot/branches{/branch}\",\n+ \"clone_url\": \"https://github.com/ursa-labs/ursabot.git\",\n+ \"collaborators_url\": \"https://api.github.com/repos/ursa-labs/ursabot/collaborators{/collaborator}\",\n+ \"comments_url\": \"https://api.github.com/repos/ursa-labs/ursabot/comments{/number}\",\n+ \"commits_url\": \"https://api.github.com/repos/ursa-labs/ursabot/commits{/sha}\",\n+ \"compare_url\": \"https://api.github.com/repos/ursa-labs/ursabot/compare/{base}...{head}\",\n+ \"contents_url\": \"https://api.github.com/repos/ursa-labs/ursabot/contents/{+path}\",\n+ \"contributors_url\": \"https://api.github.com/repos/ursa-labs/ursabot/contributors\",\n+ \"created_at\": \"2019-02-04T15:40:31Z\",\n+ \"default_branch\": \"master\",\n+ \"deployments_url\": \"https://api.github.com/repos/ursa-labs/ursabot/deployments\",\n+ \"description\": null,\n+ \"disabled\": false,\n+ \"downloads_url\": \"https://api.github.com/repos/ursa-labs/ursabot/downloads\",\n+ \"events_url\": \"https://api.github.com/repos/ursa-labs/ursabot/events\",\n+ \"fork\": false,\n+ \"forks\": 0,\n+ \"forks_count\": 0,\n+ \"forks_url\": \"https://api.github.com/repos/ursa-labs/ursabot/forks\",\n+ \"full_name\": \"ursa-labs/ursabot\",\n+ \"git_commits_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/commits{/sha}\",\n+ \"git_refs_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/refs{/sha}\",\n+ \"git_tags_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/tags{/sha}\",\n+ \"git_url\": \"git://github.com/ursa-labs/ursabot.git\",\n+ \"has_downloads\": true,\n+ \"has_issues\": true,\n+ \"has_pages\": false,\n+ \"has_projects\": true,\n+ \"has_wiki\": true,\n+ \"homepage\": null,\n+ \"hooks_url\": \"https://api.github.com/repos/ursa-labs/ursabot/hooks\",\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot\",\n+ \"id\": 169101701,\n+ \"issue_comment_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/comments{/number}\",\n+ \"issue_events_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/events{/number}\",\n+ \"issues_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues{/number}\",\n+ \"keys_url\": \"https://api.github.com/repos/ursa-labs/ursabot/keys{/key_id}\",\n+ \"labels_url\": \"https://api.github.com/repos/ursa-labs/ursabot/labels{/name}\",\n+ \"language\": \"Jupyter Notebook\",\n+ \"languages_url\": \"https://api.github.com/repos/ursa-labs/ursabot/languages\",\n+ \"license\": null,\n+ \"merges_url\": \"https://api.github.com/repos/ursa-labs/ursabot/merges\",\n+ \"milestones_url\": \"https://api.github.com/repos/ursa-labs/ursabot/milestones{/number}\",\n+ \"mirror_url\": null,\n+ \"name\": \"ursabot\",\n+ \"node_id\": \"MDEwOlJlcG9zaXRvcnkxNjkxMDE3MDE=\",\n+ \"notifications_url\": \"https://api.github.com/repos/ursa-labs/ursabot/notifications{?since,all,participating}\",\n+ \"open_issues\": 19,\n+ \"open_issues_count\": 19,\n+ \"owner\": {\n+ \"avatar_url\": \"https://avatars2.githubusercontent.com/u/46514972?v=4\",\n+ \"events_url\": \"https://api.github.com/users/ursa-labs/events{/privacy}\",\n+ \"followers_url\": \"https://api.github.com/users/ursa-labs/followers\",\n+ \"following_url\": \"https://api.github.com/users/ursa-labs/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/ursa-labs/gists{/gist_id}\",\n+ \"gravatar_id\": \"\",\n+ \"html_url\": \"https://github.com/ursa-labs\",\n+ \"id\": 46514972,\n+ \"login\": \"ursa-labs\",\n+ \"node_id\": \"MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy\",\n+ \"organizations_url\": \"https://api.github.com/users/ursa-labs/orgs\",\n+ \"received_events_url\": \"https://api.github.com/users/ursa-labs/received_events\",\n+ \"repos_url\": \"https://api.github.com/users/ursa-labs/repos\",\n+ \"site_admin\": false,\n+ \"starred_url\": \"https://api.github.com/users/ursa-labs/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/ursa-labs/subscriptions\",\n+ \"type\": \"Organization\",\n+ \"url\": \"https://api.github.com/users/ursa-labs\"\n+ },\n+ \"private\": false,\n+ \"pulls_url\": \"https://api.github.com/repos/ursa-labs/ursabot/pulls{/number}\",\n+ \"pushed_at\": \"2019-04-05T11:22:16Z\",\n+ \"releases_url\": \"https://api.github.com/repos/ursa-labs/ursabot/releases{/id}\",\n+ \"size\": 892,\n+ \"ssh_url\": \"git@github.com:ursa-labs/ursabot.git\",\n+ \"stargazers_count\": 1,\n+ \"stargazers_url\": \"https://api.github.com/repos/ursa-labs/ursabot/stargazers\",\n+ \"statuses_url\": \"https://api.github.com/repos/ursa-labs/ursabot/statuses/{sha}\",\n+ \"subscribers_url\": \"https://api.github.com/repos/ursa-labs/ursabot/subscribers\",\n+ \"subscription_url\": \"https://api.github.com/repos/ursa-labs/ursabot/subscription\",\n+ \"svn_url\": \"https://github.com/ursa-labs/ursabot\",\n+ \"tags_url\": \"https://api.github.com/repos/ursa-labs/ursabot/tags\",\n+ \"teams_url\": \"https://api.github.com/repos/ursa-labs/ursabot/teams\",\n+ \"trees_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/trees{/sha}\",\n+ \"updated_at\": \"2019-04-04T17:49:10Z\",\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot\",\n+ \"watchers\": 1,\n+ \"watchers_count\": 1\n+ },\n+ \"sender\": {\n+ \"avatar_url\": \"https://avatars1.githubusercontent.com/u/961747?v=4\",\n+ \"events_url\": \"https://api.github.com/users/kszucs/events{/privacy}\",\n+ \"followers_url\": \"https://api.github.com/users/kszucs/followers\",\n+ \"following_url\": \"https://api.github.com/users/kszucs/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/kszucs/gists{/gist_id}\",\n+ \"gravatar_id\": \"\",\n+ \"html_url\": \"https://github.com/kszucs\",\n+ \"id\": 961747,\n+ \"login\": \"kszucs\",\n+ \"node_id\": \"MDQ6VXNlcjk2MTc0Nw==\",\n+ \"organizations_url\": \"https://api.github.com/users/kszucs/orgs\",\n+ \"received_events_url\": \"https://api.github.com/users/kszucs/received_events\",\n+ \"repos_url\": \"https://api.github.com/users/kszucs/repos\",\n+ \"site_admin\": false,\n+ \"starred_url\": \"https://api.github.com/users/kszucs/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/kszucs/subscriptions\",\n+ \"type\": \"User\",\n+ \"url\": \"https://api.github.com/users/kszucs\"\n+ }\n+}"
- },
- {
- "sha": "b7de8d838332944101812ee2a46c08dd0144efe3",
- "filename": "ursabot/tests/fixtures/issue-comment-without-pull-request.json",
- "status": "added",
- "additions": 206,
- "deletions": 0,
- "changes": 206,
- "blob_url": "https://github.com/ursa-labs/ursabot/blob/70267dee34884e4b972388e1b30d57f6248c58d0/ursabot/tests/fixtures/issue-comment-without-pull-request.json",
- "raw_url": "https://github.com/ursa-labs/ursabot/raw/70267dee34884e4b972388e1b30d57f6248c58d0/ursabot/tests/fixtures/issue-comment-without-pull-request.json",
- "contents_url": "https://api.github.com/repos/ursa-labs/ursabot/contents/ursabot/tests/fixtures/issue-comment-without-pull-request.json?ref=70267dee34884e4b972388e1b30d57f6248c58d0",
- "patch": "@@ -0,0 +1,206 @@\n+{\n+ \"action\": \"created\",\n+ \"comment\": {\n+ \"author_association\": \"MEMBER\",\n+ \"body\": \"@ursabot build\",\n+ \"created_at\": \"2019-04-05T13:07:57Z\",\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot/issues/19#issuecomment-480268708\",\n+ \"id\": 480268708,\n+ \"issue_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/19\",\n+ \"node_id\": \"MDEyOklzc3VlQ29tbWVudDQ4MDI2ODcwOA==\",\n+ \"updated_at\": \"2019-04-05T13:07:57Z\",\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/comments/480268708\",\n+ \"user\": {\n+ \"avatar_url\": \"https://avatars1.githubusercontent.com/u/961747?v=4\",\n+ \"events_url\": \"https://api.github.com/users/kszucs/events{/privacy}\",\n+ \"followers_url\": \"https://api.github.com/users/kszucs/followers\",\n+ \"following_url\": \"https://api.github.com/users/kszucs/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/kszucs/gists{/gist_id}\",\n+ \"gravatar_id\": \"\",\n+ \"html_url\": \"https://github.com/kszucs\",\n+ \"id\": 961747,\n+ \"login\": \"kszucs\",\n+ \"node_id\": \"MDQ6VXNlcjk2MTc0Nw==\",\n+ \"organizations_url\": \"https://api.github.com/users/kszucs/orgs\",\n+ \"received_events_url\": \"https://api.github.com/users/kszucs/received_events\",\n+ \"repos_url\": \"https://api.github.com/users/kszucs/repos\",\n+ \"site_admin\": false,\n+ \"starred_url\": \"https://api.github.com/users/kszucs/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/kszucs/subscriptions\",\n+ \"type\": \"User\",\n+ \"url\": \"https://api.github.com/users/kszucs\"\n+ }\n+ },\n+ \"issue\": {\n+ \"assignee\": null,\n+ \"assignees\": [],\n+ \"author_association\": \"MEMBER\",\n+ \"body\": \"\",\n+ \"closed_at\": null,\n+ \"comments\": 5,\n+ \"comments_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/19/comments\",\n+ \"created_at\": \"2019-04-02T09:56:41Z\",\n+ \"events_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/19/events\",\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot/issues/19\",\n+ \"id\": 428131685,\n+ \"labels\": [],\n+ \"labels_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/19/labels{/name}\",\n+ \"locked\": false,\n+ \"milestone\": null,\n+ \"node_id\": \"MDU6SXNzdWU0MjgxMzE2ODU=\",\n+ \"number\": 19,\n+ \"repository_url\": \"https://api.github.com/repos/ursa-labs/ursabot\",\n+ \"state\": \"open\",\n+ \"title\": \"Build ursabot itself via ursabot\",\n+ \"updated_at\": \"2019-04-05T13:07:57Z\",\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/19\",\n+ \"user\": {\n+ \"avatar_url\": \"https://avatars1.githubusercontent.com/u/961747?v=4\",\n+ \"events_url\": \"https://api.github.com/users/kszucs/events{/privacy}\",\n+ \"followers_url\": \"https://api.github.com/users/kszucs/followers\",\n+ \"following_url\": \"https://api.github.com/users/kszucs/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/kszucs/gists{/gist_id}\",\n+ \"gravatar_id\": \"\",\n+ \"html_url\": \"https://github.com/kszucs\",\n+ \"id\": 961747,\n+ \"login\": \"kszucs\",\n+ \"node_id\": \"MDQ6VXNlcjk2MTc0Nw==\",\n+ \"organizations_url\": \"https://api.github.com/users/kszucs/orgs\",\n+ \"received_events_url\": \"https://api.github.com/users/kszucs/received_events\",\n+ \"repos_url\": \"https://api.github.com/users/kszucs/repos\",\n+ \"site_admin\": false,\n+ \"starred_url\": \"https://api.github.com/users/kszucs/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/kszucs/subscriptions\",\n+ \"type\": \"User\",\n+ \"url\": \"https://api.github.com/users/kszucs\"\n+ }\n+ },\n+ \"organization\": {\n+ \"avatar_url\": \"https://avatars2.githubusercontent.com/u/46514972?v=4\",\n+ \"description\": \"Innovation lab for open source data science tools, powered by Apache Arrow\",\n+ \"events_url\": \"https://api.github.com/orgs/ursa-labs/events\",\n+ \"hooks_url\": \"https://api.github.com/orgs/ursa-labs/hooks\",\n+ \"id\": 46514972,\n+ \"issues_url\": \"https://api.github.com/orgs/ursa-labs/issues\",\n+ \"login\": \"ursa-labs\",\n+ \"members_url\": \"https://api.github.com/orgs/ursa-labs/members{/member}\",\n+ \"node_id\": \"MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy\",\n+ \"public_members_url\": \"https://api.github.com/orgs/ursa-labs/public_members{/member}\",\n+ \"repos_url\": \"https://api.github.com/orgs/ursa-labs/repos\",\n+ \"url\": \"https://api.github.com/orgs/ursa-labs\"\n+ },\n+ \"repository\": {\n+ \"archive_url\": \"https://api.github.com/repos/ursa-labs/ursabot/{archive_format}{/ref}\",\n+ \"archived\": false,\n+ \"assignees_url\": \"https://api.github.com/repos/ursa-labs/ursabot/assignees{/user}\",\n+ \"blobs_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/blobs{/sha}\",\n+ \"branches_url\": \"https://api.github.com/repos/ursa-labs/ursabot/branches{/branch}\",\n+ \"clone_url\": \"https://github.com/ursa-labs/ursabot.git\",\n+ \"collaborators_url\": \"https://api.github.com/repos/ursa-labs/ursabot/collaborators{/collaborator}\",\n+ \"comments_url\": \"https://api.github.com/repos/ursa-labs/ursabot/comments{/number}\",\n+ \"commits_url\": \"https://api.github.com/repos/ursa-labs/ursabot/commits{/sha}\",\n+ \"compare_url\": \"https://api.github.com/repos/ursa-labs/ursabot/compare/{base}...{head}\",\n+ \"contents_url\": \"https://api.github.com/repos/ursa-labs/ursabot/contents/{+path}\",\n+ \"contributors_url\": \"https://api.github.com/repos/ursa-labs/ursabot/contributors\",\n+ \"created_at\": \"2019-02-04T15:40:31Z\",\n+ \"default_branch\": \"master\",\n+ \"deployments_url\": \"https://api.github.com/repos/ursa-labs/ursabot/deployments\",\n+ \"description\": null,\n+ \"disabled\": false,\n+ \"downloads_url\": \"https://api.github.com/repos/ursa-labs/ursabot/downloads\",\n+ \"events_url\": \"https://api.github.com/repos/ursa-labs/ursabot/events\",\n+ \"fork\": false,\n+ \"forks\": 0,\n+ \"forks_count\": 0,\n+ \"forks_url\": \"https://api.github.com/repos/ursa-labs/ursabot/forks\",\n+ \"full_name\": \"ursa-labs/ursabot\",\n+ \"git_commits_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/commits{/sha}\",\n+ \"git_refs_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/refs{/sha}\",\n+ \"git_tags_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/tags{/sha}\",\n+ \"git_url\": \"git://github.com/ursa-labs/ursabot.git\",\n+ \"has_downloads\": true,\n+ \"has_issues\": true,\n+ \"has_pages\": false,\n+ \"has_projects\": true,\n+ \"has_wiki\": true,\n+ \"homepage\": null,\n+ \"hooks_url\": \"https://api.github.com/repos/ursa-labs/ursabot/hooks\",\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot\",\n+ \"id\": 169101701,\n+ \"issue_comment_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/comments{/number}\",\n+ \"issue_events_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/events{/number}\",\n+ \"issues_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues{/number}\",\n+ \"keys_url\": \"https://api.github.com/repos/ursa-labs/ursabot/keys{/key_id}\",\n+ \"labels_url\": \"https://api.github.com/repos/ursa-labs/ursabot/labels{/name}\",\n+ \"language\": \"Jupyter Notebook\",\n+ \"languages_url\": \"https://api.github.com/repos/ursa-labs/ursabot/languages\",\n+ \"license\": null,\n+ \"merges_url\": \"https://api.github.com/repos/ursa-labs/ursabot/merges\",\n+ \"milestones_url\": \"https://api.github.com/repos/ursa-labs/ursabot/milestones{/number}\",\n+ \"mirror_url\": null,\n+ \"name\": \"ursabot\",\n+ \"node_id\": \"MDEwOlJlcG9zaXRvcnkxNjkxMDE3MDE=\",\n+ \"notifications_url\": \"https://api.github.com/repos/ursa-labs/ursabot/notifications{?since,all,participating}\",\n+ \"open_issues\": 19,\n+ \"open_issues_count\": 19,\n+ \"owner\": {\n+ \"avatar_url\": \"https://avatars2.githubusercontent.com/u/46514972?v=4\",\n+ \"events_url\": \"https://api.github.com/users/ursa-labs/events{/privacy}\",\n+ \"followers_url\": \"https://api.github.com/users/ursa-labs/followers\",\n+ \"following_url\": \"https://api.github.com/users/ursa-labs/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/ursa-labs/gists{/gist_id}\",\n+ \"gravatar_id\": \"\",\n+ \"html_url\": \"https://github.com/ursa-labs\",\n+ \"id\": 46514972,\n+ \"login\": \"ursa-labs\",\n+ \"node_id\": \"MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy\",\n+ \"organizations_url\": \"https://api.github.com/users/ursa-labs/orgs\",\n+ \"received_events_url\": \"https://api.github.com/users/ursa-labs/received_events\",\n+ \"repos_url\": \"https://api.github.com/users/ursa-labs/repos\",\n+ \"site_admin\": false,\n+ \"starred_url\": \"https://api.github.com/users/ursa-labs/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/ursa-labs/subscriptions\",\n+ \"type\": \"Organization\",\n+ \"url\": \"https://api.github.com/users/ursa-labs\"\n+ },\n+ \"private\": false,\n+ \"pulls_url\": \"https://api.github.com/repos/ursa-labs/ursabot/pulls{/number}\",\n+ \"pushed_at\": \"2019-04-05T12:01:40Z\",\n+ \"releases_url\": \"https://api.github.com/repos/ursa-labs/ursabot/releases{/id}\",\n+ \"size\": 898,\n+ \"ssh_url\": \"git@github.com:ursa-labs/ursabot.git\",\n+ \"stargazers_count\": 1,\n+ \"stargazers_url\": \"https://api.github.com/repos/ursa-labs/ursabot/stargazers\",\n+ \"statuses_url\": \"https://api.github.com/repos/ursa-labs/ursabot/statuses/{sha}\",\n+ \"subscribers_url\": \"https://api.github.com/repos/ursa-labs/ursabot/subscribers\",\n+ \"subscription_url\": \"https://api.github.com/repos/ursa-labs/ursabot/subscription\",\n+ \"svn_url\": \"https://github.com/ursa-labs/ursabot\",\n+ \"tags_url\": \"https://api.github.com/repos/ursa-labs/ursabot/tags\",\n+ \"teams_url\": \"https://api.github.com/repos/ursa-labs/ursabot/teams\",\n+ \"trees_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/trees{/sha}\",\n+ \"updated_at\": \"2019-04-04T17:49:10Z\",\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot\",\n+ \"watchers\": 1,\n+ \"watchers_count\": 1\n+ },\n+ \"sender\": {\n+ \"avatar_url\": \"https://avatars1.githubusercontent.com/u/961747?v=4\",\n+ \"events_url\": \"https://api.github.com/users/kszucs/events{/privacy}\",\n+ \"followers_url\": \"https://api.github.com/users/kszucs/followers\",\n+ \"following_url\": \"https://api.github.com/users/kszucs/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/kszucs/gists{/gist_id}\",\n+ \"gravatar_id\": \"\",\n+ \"html_url\": \"https://github.com/kszucs\",\n+ \"id\": 961747,\n+ \"login\": \"kszucs\",\n+ \"node_id\": \"MDQ6VXNlcjk2MTc0Nw==\",\n+ \"organizations_url\": \"https://api.github.com/users/kszucs/orgs\",\n+ \"received_events_url\": \"https://api.github.com/users/kszucs/received_events\",\n+ \"repos_url\": \"https://api.github.com/users/kszucs/repos\",\n+ \"site_admin\": false,\n+ \"starred_url\": \"https://api.github.com/users/kszucs/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/kszucs/subscriptions\",\n+ \"type\": \"User\",\n+ \"url\": \"https://api.github.com/users/kszucs\"\n+ }\n+}"
- },
- {
- "sha": "33e051455e866fb4774a16ae02ad40dcf9e6a7fd",
- "filename": "ursabot/tests/fixtures/pull-request-26-commit.json",
- "status": "added",
- "additions": 158,
- "deletions": 0,
- "changes": 158,
- "blob_url": "https://github.com/ursa-labs/ursabot/blob/70267dee34884e4b972388e1b30d57f6248c58d0/ursabot/tests/fixtures/pull-request-26-commit.json",
- "raw_url": "https://github.com/ursa-labs/ursabot/raw/70267dee34884e4b972388e1b30d57f6248c58d0/ursabot/tests/fixtures/pull-request-26-commit.json",
- "contents_url": "https://api.github.com/repos/ursa-labs/ursabot/contents/ursabot/tests/fixtures/pull-request-26-commit.json?ref=70267dee34884e4b972388e1b30d57f6248c58d0",
- "patch": "@@ -0,0 +1,158 @@\n+{\n+ \"sha\": \"2705da2b616b98fa6010a25813c5a7a27456f71d\",\n+ \"node_id\": \"MDY6Q29tbWl0MTY5MTAxNzAxOjI3MDVkYTJiNjE2Yjk4ZmE2MDEwYTI1ODEzYzVhN2EyNzQ1NmY3MWQ=\",\n+ \"commit\": {\n+ \"author\": {\n+ \"name\": \"Krisztián Szűcs\",\n+ \"email\": \"szucs.krisztian@gmail.com\",\n+ \"date\": \"2019-04-05T12:01:31Z\"\n+ },\n+ \"committer\": {\n+ \"name\": \"Krisztián Szűcs\",\n+ \"email\": \"szucs.krisztian@gmail.com\",\n+ \"date\": \"2019-04-05T12:01:31Z\"\n+ },\n+ \"message\": \"add recorded event requests\",\n+ \"tree\": {\n+ \"sha\": \"16a7bb186833a67e9c2d84a58393503b85500ceb\",\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/trees/16a7bb186833a67e9c2d84a58393503b85500ceb\"\n+ },\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/commits/2705da2b616b98fa6010a25813c5a7a27456f71d\",\n+ \"comment_count\": 0,\n+ \"verification\": {\n+ \"verified\": true,\n+ \"reason\": \"valid\",\n+ \"signature\": \"-----BEGIN PGP SIGNATURE-----\\n\\niQFOBAABCAA4FiEEOOW2r8dr6sA77zHlgjqBKYe1QKUFAlynQ58aHHN6dWNzLmty\\naXN6dGlhbkBnbWFpbC5jb20ACgkQgjqBKYe1QKUYKwf6AiXDMaLqNLNSjRY7lIXX\\nudioewz0hSb4bgIXBv30nswu9CoOA0+mHCokEVtZhYbXzXDsZ1KJrilSC4j+Ws4q\\nkRGA6iEmrne2HcSKNZXzcVnwV9zpwKxlVh2QCTNb1PuOYFBLH0kwE704uWIWMGDN\\nbo8cjQPwegePCRguCvPh/5wa5J3uiq5gmJLG6bC/d1XYE+FJVtlnyzqzLMIryGKe\\ntIciw+wwkF413Q/YVbZ49vLUeCX9H8PHC4mZYGDWuvjFW1WTfkjK5bAH+oaTVM6h\\n350I5ZFloHmMA/QeRge5qFxXoEBMDGiXHHktzYZDXnliFOQNxzqwirA5lQQ6LRSS\\naQ==\\n=7rqi\\n-----END PGP SIGNATURE-----\",\n+ \"payload\": \"tree 16a7bb186833a67e9c2d84a58393503b85500ceb\\nparent 446ae69b9385e8d0f40aa9595f723d34383af2f7\\nauthor Krisztián Szűcs <szucs.krisztian@gmail.com> 1554465691 +0200\\ncommitter Krisztián Szűcs <szucs.krisztian@gmail.com> 1554465691 +0200\\n\\nadd recorded event requests\\n\"\n+ }\n+ },\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot/commits/2705da2b616b98fa6010a25813c5a7a27456f71d\",\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot/commit/2705da2b616b98fa6010a25813c5a7a27456f71d\",\n+ \"comments_url\": \"https://api.github.com/repos/ursa-labs/ursabot/commits/2705da2b616b98fa6010a25813c5a7a27456f71d/comments\",\n+ \"author\": {\n+ \"login\": \"kszucs\",\n+ \"id\": 961747,\n+ \"node_id\": \"MDQ6VXNlcjk2MTc0Nw==\",\n+ \"avatar_url\": \"https://avatars1.githubusercontent.com/u/961747?v=4\",\n+ \"gravatar_id\": \"\",\n+ \"url\": \"https://api.github.com/users/kszucs\",\n+ \"html_url\": \"https://github.com/kszucs\",\n+ \"followers_url\": \"https://api.github.com/users/kszucs/followers\",\n+ \"following_url\": \"https://api.github.com/users/kszucs/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/kszucs/gists{/gist_id}\",\n+ \"starred_url\": \"https://api.github.com/users/kszucs/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/kszucs/subscriptions\",\n+ \"organizations_url\": \"https://api.github.com/users/kszucs/orgs\",\n+ \"repos_url\": \"https://api.github.com/users/kszucs/repos\",\n+ \"events_url\": \"https://api.github.com/users/kszucs/events{/privacy}\",\n+ \"received_events_url\": \"https://api.github.com/users/kszucs/received_events\",\n+ \"type\": \"User\",\n+ \"site_admin\": false\n+ },\n+ \"committer\": {\n+ \"login\": \"kszucs\",\n+ \"id\": 961747,\n+ \"node_id\": \"MDQ6VXNlcjk2MTc0Nw==\",\n+ \"avatar_url\": \"https://avatars1.githubusercontent.com/u/961747?v=4\",\n+ \"gravatar_id\": \"\",\n+ \"url\": \"https://api.github.com/users/kszucs\",\n+ \"html_url\": \"https://github.com/kszucs\",\n+ \"followers_url\": \"https://api.github.com/users/kszucs/followers\",\n+ \"following_url\": \"https://api.github.com/users/kszucs/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/kszucs/gists{/gist_id}\",\n+ \"starred_url\": \"https://api.github.com/users/kszucs/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/kszucs/subscriptions\",\n+ \"organizations_url\": \"https://api.github.com/users/kszucs/orgs\",\n+ \"repos_url\": \"https://api.github.com/users/kszucs/repos\",\n+ \"events_url\": \"https://api.github.com/users/kszucs/events{/privacy}\",\n+ \"received_events_url\": \"https://api.github.com/users/kszucs/received_events\",\n+ \"type\": \"User\",\n+ \"site_admin\": false\n+ },\n+ \"parents\": [\n+ {\n+ \"sha\": \"446ae69b9385e8d0f40aa9595f723d34383af2f7\",\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot/commits/446ae69b9385e8d0f40aa9595f723d34383af2f7\",\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot/commit/446ae69b9385e8d0f40aa9595f723d34383af2f7\"\n+ }\n+ ],\n+ \"stats\": {\n+ \"total\": 1062,\n+ \"additions\": 1058,\n+ \"deletions\": 4\n+ },\n+ \"files\": [\n+ {\n+ \"sha\": \"dfae6eeaef384ae6180c6302a58b49e39982dc33\",\n+ \"filename\": \"ursabot/tests/fixtures/issue-comment-build-command.json\",\n+ \"status\": \"added\",\n+ \"additions\": 212,\n+ \"deletions\": 0,\n+ \"changes\": 212,\n+ \"blob_url\": \"https://github.com/ursa-labs/ursabot/blob/2705da2b616b98fa6010a25813c5a7a27456f71d/ursabot/tests/fixtures/issue-comment-build-command.json\",\n+ \"raw_url\": \"https://github.com/ursa-labs/ursabot/raw/2705da2b616b98fa6010a25813c5a7a27456f71d/ursabot/tests/fixtures/issue-comment-build-command.json\",\n+ \"contents_url\": \"https://api.github.com/repos/ursa-labs/ursabot/contents/ursabot/tests/fixtures/issue-comment-build-command.json?ref=2705da2b616b98fa6010a25813c5a7a27456f71d\",\n+ \"patch\": \"@@ -0,0 +1,212 @@\\n+{\\n+ \\\"action\\\": \\\"created\\\",\\n+ \\\"comment\\\": {\\n+ \\\"author_association\\\": \\\"NONE\\\",\\n+ \\\"body\\\": \\\"I've successfully started builds for this PR\\\",\\n+ \\\"created_at\\\": \\\"2019-04-05T11:55:44Z\\\",\\n+ \\\"html_url\\\": \\\"https://github.com/ursa-labs/ursabot/pull/26#issuecomment-480248730\\\",\\n+ \\\"id\\\": 480248730,\\n+ \\\"issue_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues/26\\\",\\n+ \\\"node_id\\\": \\\"MDEyOklzc3VlQ29tbWVudDQ4MDI0ODczMA==\\\",\\n+ \\\"updated_at\\\": \\\"2019-04-05T11:55:44Z\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues/comments/480248730\\\",\\n+ \\\"user\\\": {\\n+ \\\"avatar_url\\\": \\\"https://avatars2.githubusercontent.com/u/49275095?v=4\\\",\\n+ \\\"events_url\\\": \\\"https://api.github.com/users/ursabot/events{/privacy}\\\",\\n+ \\\"followers_url\\\": \\\"https://api.github.com/users/ursabot/followers\\\",\\n+ \\\"following_url\\\": \\\"https://api.github.com/users/ursabot/following{/other_user}\\\",\\n+ \\\"gists_url\\\": \\\"https://api.github.com/users/ursabot/gists{/gist_id}\\\",\\n+ \\\"gravatar_id\\\": \\\"\\\",\\n+ \\\"html_url\\\": \\\"https://github.com/ursabot\\\",\\n+ \\\"id\\\": 49275095,\\n+ \\\"login\\\": \\\"ursabot\\\",\\n+ \\\"node_id\\\": \\\"MDQ6VXNlcjQ5Mjc1MDk1\\\",\\n+ \\\"organizations_url\\\": \\\"https://api.github.com/users/ursabot/orgs\\\",\\n+ \\\"received_events_url\\\": \\\"https://api.github.com/users/ursabot/received_events\\\",\\n+ \\\"repos_url\\\": \\\"https://api.github.com/users/ursabot/repos\\\",\\n+ \\\"site_admin\\\": false,\\n+ \\\"starred_url\\\": \\\"https://api.github.com/users/ursabot/starred{/owner}{/repo}\\\",\\n+ \\\"subscriptions_url\\\": \\\"https://api.github.com/users/ursabot/subscriptions\\\",\\n+ \\\"type\\\": \\\"User\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/users/ursabot\\\"\\n+ }\\n+ },\\n+ \\\"issue\\\": {\\n+ \\\"assignee\\\": null,\\n+ \\\"assignees\\\": [],\\n+ \\\"author_association\\\": \\\"MEMBER\\\",\\n+ \\\"body\\\": \\\"\\\",\\n+ \\\"closed_at\\\": null,\\n+ \\\"comments\\\": 4,\\n+ \\\"comments_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues/26/comments\\\",\\n+ \\\"created_at\\\": \\\"2019-04-05T11:22:15Z\\\",\\n+ \\\"events_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues/26/events\\\",\\n+ \\\"html_url\\\": \\\"https://github.com/ursa-labs/ursabot/pull/26\\\",\\n+ \\\"id\\\": 429706959,\\n+ \\\"labels\\\": [],\\n+ \\\"labels_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues/26/labels{/name}\\\",\\n+ \\\"locked\\\": false,\\n+ \\\"milestone\\\": null,\\n+ \\\"node_id\\\": \\\"MDExOlB1bGxSZXF1ZXN0MjY3Nzg1NTUy\\\",\\n+ \\\"number\\\": 26,\\n+ \\\"pull_request\\\": {\\n+ \\\"diff_url\\\": \\\"https://github.com/ursa-labs/ursabot/pull/26.diff\\\",\\n+ \\\"html_url\\\": \\\"https://github.com/ursa-labs/ursabot/pull/26\\\",\\n+ \\\"patch_url\\\": \\\"https://github.com/ursa-labs/ursabot/pull/26.patch\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/pulls/26\\\"\\n+ },\\n+ \\\"repository_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot\\\",\\n+ \\\"state\\\": \\\"open\\\",\\n+ \\\"title\\\": \\\"Unittests for GithubHook\\\",\\n+ \\\"updated_at\\\": \\\"2019-04-05T11:55:44Z\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues/26\\\",\\n+ \\\"user\\\": {\\n+ \\\"avatar_url\\\": \\\"https://avatars1.githubusercontent.com/u/961747?v=4\\\",\\n+ \\\"events_url\\\": \\\"https://api.github.com/users/kszucs/events{/privacy}\\\",\\n+ \\\"followers_url\\\": \\\"https://api.github.com/users/kszucs/followers\\\",\\n+ \\\"following_url\\\": \\\"https://api.github.com/users/kszucs/following{/other_user}\\\",\\n+ \\\"gists_url\\\": \\\"https://api.github.com/users/kszucs/gists{/gist_id}\\\",\\n+ \\\"gravatar_id\\\": \\\"\\\",\\n+ \\\"html_url\\\": \\\"https://github.com/kszucs\\\",\\n+ \\\"id\\\": 961747,\\n+ \\\"login\\\": \\\"kszucs\\\",\\n+ \\\"node_id\\\": \\\"MDQ6VXNlcjk2MTc0Nw==\\\",\\n+ \\\"organizations_url\\\": \\\"https://api.github.com/users/kszucs/orgs\\\",\\n+ \\\"received_events_url\\\": \\\"https://api.github.com/users/kszucs/received_events\\\",\\n+ \\\"repos_url\\\": \\\"https://api.github.com/users/kszucs/repos\\\",\\n+ \\\"site_admin\\\": false,\\n+ \\\"starred_url\\\": \\\"https://api.github.com/users/kszucs/starred{/owner}{/repo}\\\",\\n+ \\\"subscriptions_url\\\": \\\"https://api.github.com/users/kszucs/subscriptions\\\",\\n+ \\\"type\\\": \\\"User\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/users/kszucs\\\"\\n+ }\\n+ },\\n+ \\\"organization\\\": {\\n+ \\\"avatar_url\\\": \\\"https://avatars2.githubusercontent.com/u/46514972?v=4\\\",\\n+ \\\"description\\\": \\\"Innovation lab for open source data science tools, powered by Apache Arrow\\\",\\n+ \\\"events_url\\\": \\\"https://api.github.com/orgs/ursa-labs/events\\\",\\n+ \\\"hooks_url\\\": \\\"https://api.github.com/orgs/ursa-labs/hooks\\\",\\n+ \\\"id\\\": 46514972,\\n+ \\\"issues_url\\\": \\\"https://api.github.com/orgs/ursa-labs/issues\\\",\\n+ \\\"login\\\": \\\"ursa-labs\\\",\\n+ \\\"members_url\\\": \\\"https://api.github.com/orgs/ursa-labs/members{/member}\\\",\\n+ \\\"node_id\\\": \\\"MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy\\\",\\n+ \\\"public_members_url\\\": \\\"https://api.github.com/orgs/ursa-labs/public_members{/member}\\\",\\n+ \\\"repos_url\\\": \\\"https://api.github.com/orgs/ursa-labs/repos\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/orgs/ursa-labs\\\"\\n+ },\\n+ \\\"repository\\\": {\\n+ \\\"archive_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/{archive_format}{/ref}\\\",\\n+ \\\"archived\\\": false,\\n+ \\\"assignees_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/assignees{/user}\\\",\\n+ \\\"blobs_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/git/blobs{/sha}\\\",\\n+ \\\"branches_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/branches{/branch}\\\",\\n+ \\\"clone_url\\\": \\\"https://github.com/ursa-labs/ursabot.git\\\",\\n+ \\\"collaborators_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/collaborators{/collaborator}\\\",\\n+ \\\"comments_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/comments{/number}\\\",\\n+ \\\"commits_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/commits{/sha}\\\",\\n+ \\\"compare_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/compare/{base}...{head}\\\",\\n+ \\\"contents_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/contents/{+path}\\\",\\n+ \\\"contributors_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/contributors\\\",\\n+ \\\"created_at\\\": \\\"2019-02-04T15:40:31Z\\\",\\n+ \\\"default_branch\\\": \\\"master\\\",\\n+ \\\"deployments_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/deployments\\\",\\n+ \\\"description\\\": null,\\n+ \\\"disabled\\\": false,\\n+ \\\"downloads_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/downloads\\\",\\n+ \\\"events_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/events\\\",\\n+ \\\"fork\\\": false,\\n+ \\\"forks\\\": 0,\\n+ \\\"forks_count\\\": 0,\\n+ \\\"forks_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/forks\\\",\\n+ \\\"full_name\\\": \\\"ursa-labs/ursabot\\\",\\n+ \\\"git_commits_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/git/commits{/sha}\\\",\\n+ \\\"git_refs_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/git/refs{/sha}\\\",\\n+ \\\"git_tags_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/git/tags{/sha}\\\",\\n+ \\\"git_url\\\": \\\"git://github.com/ursa-labs/ursabot.git\\\",\\n+ \\\"has_downloads\\\": true,\\n+ \\\"has_issues\\\": true,\\n+ \\\"has_pages\\\": false,\\n+ \\\"has_projects\\\": true,\\n+ \\\"has_wiki\\\": true,\\n+ \\\"homepage\\\": null,\\n+ \\\"hooks_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/hooks\\\",\\n+ \\\"html_url\\\": \\\"https://github.com/ursa-labs/ursabot\\\",\\n+ \\\"id\\\": 169101701,\\n+ \\\"issue_comment_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues/comments{/number}\\\",\\n+ \\\"issue_events_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues/events{/number}\\\",\\n+ \\\"issues_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues{/number}\\\",\\n+ \\\"keys_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/keys{/key_id}\\\",\\n+ \\\"labels_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/labels{/name}\\\",\\n+ \\\"language\\\": \\\"Jupyter Notebook\\\",\\n+ \\\"languages_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/languages\\\",\\n+ \\\"license\\\": null,\\n+ \\\"merges_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/merges\\\",\\n+ \\\"milestones_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/milestones{/number}\\\",\\n+ \\\"mirror_url\\\": null,\\n+ \\\"name\\\": \\\"ursabot\\\",\\n+ \\\"node_id\\\": \\\"MDEwOlJlcG9zaXRvcnkxNjkxMDE3MDE=\\\",\\n+ \\\"notifications_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/notifications{?since,all,participating}\\\",\\n+ \\\"open_issues\\\": 19,\\n+ \\\"open_issues_count\\\": 19,\\n+ \\\"owner\\\": {\\n+ \\\"avatar_url\\\": \\\"https://avatars2.githubusercontent.com/u/46514972?v=4\\\",\\n+ \\\"events_url\\\": \\\"https://api.github.com/users/ursa-labs/events{/privacy}\\\",\\n+ \\\"followers_url\\\": \\\"https://api.github.com/users/ursa-labs/followers\\\",\\n+ \\\"following_url\\\": \\\"https://api.github.com/users/ursa-labs/following{/other_user}\\\",\\n+ \\\"gists_url\\\": \\\"https://api.github.com/users/ursa-labs/gists{/gist_id}\\\",\\n+ \\\"gravatar_id\\\": \\\"\\\",\\n+ \\\"html_url\\\": \\\"https://github.com/ursa-labs\\\",\\n+ \\\"id\\\": 46514972,\\n+ \\\"login\\\": \\\"ursa-labs\\\",\\n+ \\\"node_id\\\": \\\"MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy\\\",\\n+ \\\"organizations_url\\\": \\\"https://api.github.com/users/ursa-labs/orgs\\\",\\n+ \\\"received_events_url\\\": \\\"https://api.github.com/users/ursa-labs/received_events\\\",\\n+ \\\"repos_url\\\": \\\"https://api.github.com/users/ursa-labs/repos\\\",\\n+ \\\"site_admin\\\": false,\\n+ \\\"starred_url\\\": \\\"https://api.github.com/users/ursa-labs/starred{/owner}{/repo}\\\",\\n+ \\\"subscriptions_url\\\": \\\"https://api.github.com/users/ursa-labs/subscriptions\\\",\\n+ \\\"type\\\": \\\"Organization\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/users/ursa-labs\\\"\\n+ },\\n+ \\\"private\\\": false,\\n+ \\\"pulls_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/pulls{/number}\\\",\\n+ \\\"pushed_at\\\": \\\"2019-04-05T11:22:16Z\\\",\\n+ \\\"releases_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/releases{/id}\\\",\\n+ \\\"size\\\": 892,\\n+ \\\"ssh_url\\\": \\\"git@github.com:ursa-labs/ursabot.git\\\",\\n+ \\\"stargazers_count\\\": 1,\\n+ \\\"stargazers_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/stargazers\\\",\\n+ \\\"statuses_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/statuses/{sha}\\\",\\n+ \\\"subscribers_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/subscribers\\\",\\n+ \\\"subscription_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/subscription\\\",\\n+ \\\"svn_url\\\": \\\"https://github.com/ursa-labs/ursabot\\\",\\n+ \\\"tags_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/tags\\\",\\n+ \\\"teams_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/teams\\\",\\n+ \\\"trees_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/git/trees{/sha}\\\",\\n+ \\\"updated_at\\\": \\\"2019-04-04T17:49:10Z\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot\\\",\\n+ \\\"watchers\\\": 1,\\n+ \\\"watchers_count\\\": 1\\n+ },\\n+ \\\"sender\\\": {\\n+ \\\"avatar_url\\\": \\\"https://avatars2.githubusercontent.com/u/49275095?v=4\\\",\\n+ \\\"events_url\\\": \\\"https://api.github.com/users/ursabot/events{/privacy}\\\",\\n+ \\\"followers_url\\\": \\\"https://api.github.com/users/ursabot/followers\\\",\\n+ \\\"following_url\\\": \\\"https://api.github.com/users/ursabot/following{/other_user}\\\",\\n+ \\\"gists_url\\\": \\\"https://api.github.com/users/ursabot/gists{/gist_id}\\\",\\n+ \\\"gravatar_id\\\": \\\"\\\",\\n+ \\\"html_url\\\": \\\"https://github.com/ursabot\\\",\\n+ \\\"id\\\": 49275095,\\n+ \\\"login\\\": \\\"ursabot\\\",\\n+ \\\"node_id\\\": \\\"MDQ6VXNlcjQ5Mjc1MDk1\\\",\\n+ \\\"organizations_url\\\": \\\"https://api.github.com/users/ursabot/orgs\\\",\\n+ \\\"received_events_url\\\": \\\"https://api.github.com/users/ursabot/received_events\\\",\\n+ \\\"repos_url\\\": \\\"https://api.github.com/users/ursabot/repos\\\",\\n+ \\\"site_admin\\\": false,\\n+ \\\"starred_url\\\": \\\"https://api.github.com/users/ursabot/starred{/owner}{/repo}\\\",\\n+ \\\"subscriptions_url\\\": \\\"https://api.github.com/users/ursabot/subscriptions\\\",\\n+ \\\"type\\\": \\\"User\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/users/ursabot\\\"\\n+ }\\n+}\"\n+ },\n+ {\n+ \"sha\": \"7ef554e333327f0e62aa1fd76b4b17844a39adeb\",\n+ \"filename\": \"ursabot/tests/fixtures/issue-comment-by-ursabot.json\",\n+ \"status\": \"added\",\n+ \"additions\": 212,\n+ \"deletions\": 0,\n+ \"changes\": 212,\n+ \"blob_url\": \"https://github.com/ursa-labs/ursabot/blob/2705da2b616b98fa6010a25813c5a7a27456f71d/ursabot/tests/fixtures/issue-comment-by-ursabot.json\",\n+ \"raw_url\": \"https://github.com/ursa-labs/ursabot/raw/2705da2b616b98fa6010a25813c5a7a27456f71d/ursabot/tests/fixtures/issue-comment-by-ursabot.json\",\n+ \"contents_url\": \"https://api.github.com/repos/ursa-labs/ursabot/contents/ursabot/tests/fixtures/issue-comment-by-ursabot.json?ref=2705da2b616b98fa6010a25813c5a7a27456f71d\",\n+ \"patch\": \"@@ -0,0 +1,212 @@\\n+{\\n+ \\\"action\\\": \\\"created\\\",\\n+ \\\"comment\\\": {\\n+ \\\"author_association\\\": \\\"NONE\\\",\\n+ \\\"body\\\": \\\"Unknown command \\\\\\\"\\\\\\\"\\\",\\n+ \\\"created_at\\\": \\\"2019-04-05T11:35:47Z\\\",\\n+ \\\"html_url\\\": \\\"https://github.com/ursa-labs/ursabot/pull/26#issuecomment-480243815\\\",\\n+ \\\"id\\\": 480243815,\\n+ \\\"issue_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues/26\\\",\\n+ \\\"node_id\\\": \\\"MDEyOklzc3VlQ29tbWVudDQ4MDI0MzgxNQ==\\\",\\n+ \\\"updated_at\\\": \\\"2019-04-05T11:35:47Z\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues/comments/480243815\\\",\\n+ \\\"user\\\": {\\n+ \\\"avatar_url\\\": \\\"https://avatars2.githubusercontent.com/u/49275095?v=4\\\",\\n+ \\\"events_url\\\": \\\"https://api.github.com/users/ursabot/events{/privacy}\\\",\\n+ \\\"followers_url\\\": \\\"https://api.github.com/users/ursabot/followers\\\",\\n+ \\\"following_url\\\": \\\"https://api.github.com/users/ursabot/following{/other_user}\\\",\\n+ \\\"gists_url\\\": \\\"https://api.github.com/users/ursabot/gists{/gist_id}\\\",\\n+ \\\"gravatar_id\\\": \\\"\\\",\\n+ \\\"html_url\\\": \\\"https://github.com/ursabot\\\",\\n+ \\\"id\\\": 49275095,\\n+ \\\"login\\\": \\\"ursabot\\\",\\n+ \\\"node_id\\\": \\\"MDQ6VXNlcjQ5Mjc1MDk1\\\",\\n+ \\\"organizations_url\\\": \\\"https://api.github.com/users/ursabot/orgs\\\",\\n+ \\\"received_events_url\\\": \\\"https://api.github.com/users/ursabot/received_events\\\",\\n+ \\\"repos_url\\\": \\\"https://api.github.com/users/ursabot/repos\\\",\\n+ \\\"site_admin\\\": false,\\n+ \\\"starred_url\\\": \\\"https://api.github.com/users/ursabot/starred{/owner}{/repo}\\\",\\n+ \\\"subscriptions_url\\\": \\\"https://api.github.com/users/ursabot/subscriptions\\\",\\n+ \\\"type\\\": \\\"User\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/users/ursabot\\\"\\n+ }\\n+ },\\n+ \\\"issue\\\": {\\n+ \\\"assignee\\\": null,\\n+ \\\"assignees\\\": [],\\n+ \\\"author_association\\\": \\\"MEMBER\\\",\\n+ \\\"body\\\": \\\"\\\",\\n+ \\\"closed_at\\\": null,\\n+ \\\"comments\\\": 2,\\n+ \\\"comments_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues/26/comments\\\",\\n+ \\\"created_at\\\": \\\"2019-04-05T11:22:15Z\\\",\\n+ \\\"events_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues/26/events\\\",\\n+ \\\"html_url\\\": \\\"https://github.com/ursa-labs/ursabot/pull/26\\\",\\n+ \\\"id\\\": 429706959,\\n+ \\\"labels\\\": [],\\n+ \\\"labels_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues/26/labels{/name}\\\",\\n+ \\\"locked\\\": false,\\n+ \\\"milestone\\\": null,\\n+ \\\"node_id\\\": \\\"MDExOlB1bGxSZXF1ZXN0MjY3Nzg1NTUy\\\",\\n+ \\\"number\\\": 26,\\n+ \\\"pull_request\\\": {\\n+ \\\"diff_url\\\": \\\"https://github.com/ursa-labs/ursabot/pull/26.diff\\\",\\n+ \\\"html_url\\\": \\\"https://github.com/ursa-labs/ursabot/pull/26\\\",\\n+ \\\"patch_url\\\": \\\"https://github.com/ursa-labs/ursabot/pull/26.patch\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/pulls/26\\\"\\n+ },\\n+ \\\"repository_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot\\\",\\n+ \\\"state\\\": \\\"open\\\",\\n+ \\\"title\\\": \\\"Unittests for GithubHook\\\",\\n+ \\\"updated_at\\\": \\\"2019-04-05T11:35:47Z\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues/26\\\",\\n+ \\\"user\\\": {\\n+ \\\"avatar_url\\\": \\\"https://avatars1.githubusercontent.com/u/961747?v=4\\\",\\n+ \\\"events_url\\\": \\\"https://api.github.com/users/kszucs/events{/privacy}\\\",\\n+ \\\"followers_url\\\": \\\"https://api.github.com/users/kszucs/followers\\\",\\n+ \\\"following_url\\\": \\\"https://api.github.com/users/kszucs/following{/other_user}\\\",\\n+ \\\"gists_url\\\": \\\"https://api.github.com/users/kszucs/gists{/gist_id}\\\",\\n+ \\\"gravatar_id\\\": \\\"\\\",\\n+ \\\"html_url\\\": \\\"https://github.com/kszucs\\\",\\n+ \\\"id\\\": 961747,\\n+ \\\"login\\\": \\\"kszucs\\\",\\n+ \\\"node_id\\\": \\\"MDQ6VXNlcjk2MTc0Nw==\\\",\\n+ \\\"organizations_url\\\": \\\"https://api.github.com/users/kszucs/orgs\\\",\\n+ \\\"received_events_url\\\": \\\"https://api.github.com/users/kszucs/received_events\\\",\\n+ \\\"repos_url\\\": \\\"https://api.github.com/users/kszucs/repos\\\",\\n+ \\\"site_admin\\\": false,\\n+ \\\"starred_url\\\": \\\"https://api.github.com/users/kszucs/starred{/owner}{/repo}\\\",\\n+ \\\"subscriptions_url\\\": \\\"https://api.github.com/users/kszucs/subscriptions\\\",\\n+ \\\"type\\\": \\\"User\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/users/kszucs\\\"\\n+ }\\n+ },\\n+ \\\"organization\\\": {\\n+ \\\"avatar_url\\\": \\\"https://avatars2.githubusercontent.com/u/46514972?v=4\\\",\\n+ \\\"description\\\": \\\"Innovation lab for open source data science tools, powered by Apache Arrow\\\",\\n+ \\\"events_url\\\": \\\"https://api.github.com/orgs/ursa-labs/events\\\",\\n+ \\\"hooks_url\\\": \\\"https://api.github.com/orgs/ursa-labs/hooks\\\",\\n+ \\\"id\\\": 46514972,\\n+ \\\"issues_url\\\": \\\"https://api.github.com/orgs/ursa-labs/issues\\\",\\n+ \\\"login\\\": \\\"ursa-labs\\\",\\n+ \\\"members_url\\\": \\\"https://api.github.com/orgs/ursa-labs/members{/member}\\\",\\n+ \\\"node_id\\\": \\\"MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy\\\",\\n+ \\\"public_members_url\\\": \\\"https://api.github.com/orgs/ursa-labs/public_members{/member}\\\",\\n+ \\\"repos_url\\\": \\\"https://api.github.com/orgs/ursa-labs/repos\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/orgs/ursa-labs\\\"\\n+ },\\n+ \\\"repository\\\": {\\n+ \\\"archive_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/{archive_format}{/ref}\\\",\\n+ \\\"archived\\\": false,\\n+ \\\"assignees_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/assignees{/user}\\\",\\n+ \\\"blobs_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/git/blobs{/sha}\\\",\\n+ \\\"branches_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/branches{/branch}\\\",\\n+ \\\"clone_url\\\": \\\"https://github.com/ursa-labs/ursabot.git\\\",\\n+ \\\"collaborators_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/collaborators{/collaborator}\\\",\\n+ \\\"comments_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/comments{/number}\\\",\\n+ \\\"commits_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/commits{/sha}\\\",\\n+ \\\"compare_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/compare/{base}...{head}\\\",\\n+ \\\"contents_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/contents/{+path}\\\",\\n+ \\\"contributors_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/contributors\\\",\\n+ \\\"created_at\\\": \\\"2019-02-04T15:40:31Z\\\",\\n+ \\\"default_branch\\\": \\\"master\\\",\\n+ \\\"deployments_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/deployments\\\",\\n+ \\\"description\\\": null,\\n+ \\\"disabled\\\": false,\\n+ \\\"downloads_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/downloads\\\",\\n+ \\\"events_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/events\\\",\\n+ \\\"fork\\\": false,\\n+ \\\"forks\\\": 0,\\n+ \\\"forks_count\\\": 0,\\n+ \\\"forks_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/forks\\\",\\n+ \\\"full_name\\\": \\\"ursa-labs/ursabot\\\",\\n+ \\\"git_commits_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/git/commits{/sha}\\\",\\n+ \\\"git_refs_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/git/refs{/sha}\\\",\\n+ \\\"git_tags_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/git/tags{/sha}\\\",\\n+ \\\"git_url\\\": \\\"git://github.com/ursa-labs/ursabot.git\\\",\\n+ \\\"has_downloads\\\": true,\\n+ \\\"has_issues\\\": true,\\n+ \\\"has_pages\\\": false,\\n+ \\\"has_projects\\\": true,\\n+ \\\"has_wiki\\\": true,\\n+ \\\"homepage\\\": null,\\n+ \\\"hooks_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/hooks\\\",\\n+ \\\"html_url\\\": \\\"https://github.com/ursa-labs/ursabot\\\",\\n+ \\\"id\\\": 169101701,\\n+ \\\"issue_comment_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues/comments{/number}\\\",\\n+ \\\"issue_events_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues/events{/number}\\\",\\n+ \\\"issues_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues{/number}\\\",\\n+ \\\"keys_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/keys{/key_id}\\\",\\n+ \\\"labels_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/labels{/name}\\\",\\n+ \\\"language\\\": \\\"Jupyter Notebook\\\",\\n+ \\\"languages_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/languages\\\",\\n+ \\\"license\\\": null,\\n+ \\\"merges_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/merges\\\",\\n+ \\\"milestones_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/milestones{/number}\\\",\\n+ \\\"mirror_url\\\": null,\\n+ \\\"name\\\": \\\"ursabot\\\",\\n+ \\\"node_id\\\": \\\"MDEwOlJlcG9zaXRvcnkxNjkxMDE3MDE=\\\",\\n+ \\\"notifications_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/notifications{?since,all,participating}\\\",\\n+ \\\"open_issues\\\": 19,\\n+ \\\"open_issues_count\\\": 19,\\n+ \\\"owner\\\": {\\n+ \\\"avatar_url\\\": \\\"https://avatars2.githubusercontent.com/u/46514972?v=4\\\",\\n+ \\\"events_url\\\": \\\"https://api.github.com/users/ursa-labs/events{/privacy}\\\",\\n+ \\\"followers_url\\\": \\\"https://api.github.com/users/ursa-labs/followers\\\",\\n+ \\\"following_url\\\": \\\"https://api.github.com/users/ursa-labs/following{/other_user}\\\",\\n+ \\\"gists_url\\\": \\\"https://api.github.com/users/ursa-labs/gists{/gist_id}\\\",\\n+ \\\"gravatar_id\\\": \\\"\\\",\\n+ \\\"html_url\\\": \\\"https://github.com/ursa-labs\\\",\\n+ \\\"id\\\": 46514972,\\n+ \\\"login\\\": \\\"ursa-labs\\\",\\n+ \\\"node_id\\\": \\\"MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy\\\",\\n+ \\\"organizations_url\\\": \\\"https://api.github.com/users/ursa-labs/orgs\\\",\\n+ \\\"received_events_url\\\": \\\"https://api.github.com/users/ursa-labs/received_events\\\",\\n+ \\\"repos_url\\\": \\\"https://api.github.com/users/ursa-labs/repos\\\",\\n+ \\\"site_admin\\\": false,\\n+ \\\"starred_url\\\": \\\"https://api.github.com/users/ursa-labs/starred{/owner}{/repo}\\\",\\n+ \\\"subscriptions_url\\\": \\\"https://api.github.com/users/ursa-labs/subscriptions\\\",\\n+ \\\"type\\\": \\\"Organization\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/users/ursa-labs\\\"\\n+ },\\n+ \\\"private\\\": false,\\n+ \\\"pulls_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/pulls{/number}\\\",\\n+ \\\"pushed_at\\\": \\\"2019-04-05T11:22:16Z\\\",\\n+ \\\"releases_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/releases{/id}\\\",\\n+ \\\"size\\\": 892,\\n+ \\\"ssh_url\\\": \\\"git@github.com:ursa-labs/ursabot.git\\\",\\n+ \\\"stargazers_count\\\": 1,\\n+ \\\"stargazers_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/stargazers\\\",\\n+ \\\"statuses_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/statuses/{sha}\\\",\\n+ \\\"subscribers_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/subscribers\\\",\\n+ \\\"subscription_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/subscription\\\",\\n+ \\\"svn_url\\\": \\\"https://github.com/ursa-labs/ursabot\\\",\\n+ \\\"tags_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/tags\\\",\\n+ \\\"teams_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/teams\\\",\\n+ \\\"trees_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/git/trees{/sha}\\\",\\n+ \\\"updated_at\\\": \\\"2019-04-04T17:49:10Z\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot\\\",\\n+ \\\"watchers\\\": 1,\\n+ \\\"watchers_count\\\": 1\\n+ },\\n+ \\\"sender\\\": {\\n+ \\\"avatar_url\\\": \\\"https://avatars2.githubusercontent.com/u/49275095?v=4\\\",\\n+ \\\"events_url\\\": \\\"https://api.github.com/users/ursabot/events{/privacy}\\\",\\n+ \\\"followers_url\\\": \\\"https://api.github.com/users/ursabot/followers\\\",\\n+ \\\"following_url\\\": \\\"https://api.github.com/users/ursabot/following{/other_user}\\\",\\n+ \\\"gists_url\\\": \\\"https://api.github.com/users/ursabot/gists{/gist_id}\\\",\\n+ \\\"gravatar_id\\\": \\\"\\\",\\n+ \\\"html_url\\\": \\\"https://github.com/ursabot\\\",\\n+ \\\"id\\\": 49275095,\\n+ \\\"login\\\": \\\"ursabot\\\",\\n+ \\\"node_id\\\": \\\"MDQ6VXNlcjQ5Mjc1MDk1\\\",\\n+ \\\"organizations_url\\\": \\\"https://api.github.com/users/ursabot/orgs\\\",\\n+ \\\"received_events_url\\\": \\\"https://api.github.com/users/ursabot/received_events\\\",\\n+ \\\"repos_url\\\": \\\"https://api.github.com/users/ursabot/repos\\\",\\n+ \\\"site_admin\\\": false,\\n+ \\\"starred_url\\\": \\\"https://api.github.com/users/ursabot/starred{/owner}{/repo}\\\",\\n+ \\\"subscriptions_url\\\": \\\"https://api.github.com/users/ursabot/subscriptions\\\",\\n+ \\\"type\\\": \\\"User\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/users/ursabot\\\"\\n+ }\\n+}\"\n+ },\n+ {\n+ \"sha\": \"a8082dbc91fdfe815b795e49ec10e49000771ef5\",\n+ \"filename\": \"ursabot/tests/fixtures/issue-comment-not-mentioning-ursabot.json\",\n+ \"status\": \"added\",\n+ \"additions\": 212,\n+ \"deletions\": 0,\n+ \"changes\": 212,\n+ \"blob_url\": \"https://github.com/ursa-labs/ursabot/blob/2705da2b616b98fa6010a25813c5a7a27456f71d/ursabot/tests/fixtures/issue-comment-not-mentioning-ursabot.json\",\n+ \"raw_url\": \"https://github.com/ursa-labs/ursabot/raw/2705da2b616b98fa6010a25813c5a7a27456f71d/ursabot/tests/fixtures/issue-comment-not-mentioning-ursabot.json\",\n+ \"contents_url\": \"https://api.github.com/repos/ursa-labs/ursabot/contents/ursabot/tests/fixtures/issue-comment-not-mentioning-ursabot.json?ref=2705da2b616b98fa6010a25813c5a7a27456f71d\",\n+ \"patch\": \"@@ -0,0 +1,212 @@\\n+{\\n+ \\\"action\\\": \\\"created\\\",\\n+ \\\"comment\\\": {\\n+ \\\"author_association\\\": \\\"MEMBER\\\",\\n+ \\\"body\\\": \\\"bear is no game\\\",\\n+ \\\"created_at\\\": \\\"2019-04-05T11:26:56Z\\\",\\n+ \\\"html_url\\\": \\\"https://github.com/ursa-labs/ursabot/pull/26#issuecomment-480241727\\\",\\n+ \\\"id\\\": 480241727,\\n+ \\\"issue_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues/26\\\",\\n+ \\\"node_id\\\": \\\"MDEyOklzc3VlQ29tbWVudDQ4MDI0MTcyNw==\\\",\\n+ \\\"updated_at\\\": \\\"2019-04-05T11:26:56Z\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues/comments/480241727\\\",\\n+ \\\"user\\\": {\\n+ \\\"avatar_url\\\": \\\"https://avatars1.githubusercontent.com/u/961747?v=4\\\",\\n+ \\\"events_url\\\": \\\"https://api.github.com/users/kszucs/events{/privacy}\\\",\\n+ \\\"followers_url\\\": \\\"https://api.github.com/users/kszucs/followers\\\",\\n+ \\\"following_url\\\": \\\"https://api.github.com/users/kszucs/following{/other_user}\\\",\\n+ \\\"gists_url\\\": \\\"https://api.github.com/users/kszucs/gists{/gist_id}\\\",\\n+ \\\"gravatar_id\\\": \\\"\\\",\\n+ \\\"html_url\\\": \\\"https://github.com/kszucs\\\",\\n+ \\\"id\\\": 961747,\\n+ \\\"login\\\": \\\"kszucs\\\",\\n+ \\\"node_id\\\": \\\"MDQ6VXNlcjk2MTc0Nw==\\\",\\n+ \\\"organizations_url\\\": \\\"https://api.github.com/users/kszucs/orgs\\\",\\n+ \\\"received_events_url\\\": \\\"https://api.github.com/users/kszucs/received_events\\\",\\n+ \\\"repos_url\\\": \\\"https://api.github.com/users/kszucs/repos\\\",\\n+ \\\"site_admin\\\": false,\\n+ \\\"starred_url\\\": \\\"https://api.github.com/users/kszucs/starred{/owner}{/repo}\\\",\\n+ \\\"subscriptions_url\\\": \\\"https://api.github.com/users/kszucs/subscriptions\\\",\\n+ \\\"type\\\": \\\"User\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/users/kszucs\\\"\\n+ }\\n+ },\\n+ \\\"issue\\\": {\\n+ \\\"assignee\\\": null,\\n+ \\\"assignees\\\": [],\\n+ \\\"author_association\\\": \\\"MEMBER\\\",\\n+ \\\"body\\\": \\\"\\\",\\n+ \\\"closed_at\\\": null,\\n+ \\\"comments\\\": 0,\\n+ \\\"comments_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues/26/comments\\\",\\n+ \\\"created_at\\\": \\\"2019-04-05T11:22:15Z\\\",\\n+ \\\"events_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues/26/events\\\",\\n+ \\\"html_url\\\": \\\"https://github.com/ursa-labs/ursabot/pull/26\\\",\\n+ \\\"id\\\": 429706959,\\n+ \\\"labels\\\": [],\\n+ \\\"labels_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues/26/labels{/name}\\\",\\n+ \\\"locked\\\": false,\\n+ \\\"milestone\\\": null,\\n+ \\\"node_id\\\": \\\"MDExOlB1bGxSZXF1ZXN0MjY3Nzg1NTUy\\\",\\n+ \\\"number\\\": 26,\\n+ \\\"pull_request\\\": {\\n+ \\\"diff_url\\\": \\\"https://github.com/ursa-labs/ursabot/pull/26.diff\\\",\\n+ \\\"html_url\\\": \\\"https://github.com/ursa-labs/ursabot/pull/26\\\",\\n+ \\\"patch_url\\\": \\\"https://github.com/ursa-labs/ursabot/pull/26.patch\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/pulls/26\\\"\\n+ },\\n+ \\\"repository_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot\\\",\\n+ \\\"state\\\": \\\"open\\\",\\n+ \\\"title\\\": \\\"Unittests for GithubHook\\\",\\n+ \\\"updated_at\\\": \\\"2019-04-05T11:26:56Z\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues/26\\\",\\n+ \\\"user\\\": {\\n+ \\\"avatar_url\\\": \\\"https://avatars1.githubusercontent.com/u/961747?v=4\\\",\\n+ \\\"events_url\\\": \\\"https://api.github.com/users/kszucs/events{/privacy}\\\",\\n+ \\\"followers_url\\\": \\\"https://api.github.com/users/kszucs/followers\\\",\\n+ \\\"following_url\\\": \\\"https://api.github.com/users/kszucs/following{/other_user}\\\",\\n+ \\\"gists_url\\\": \\\"https://api.github.com/users/kszucs/gists{/gist_id}\\\",\\n+ \\\"gravatar_id\\\": \\\"\\\",\\n+ \\\"html_url\\\": \\\"https://github.com/kszucs\\\",\\n+ \\\"id\\\": 961747,\\n+ \\\"login\\\": \\\"kszucs\\\",\\n+ \\\"node_id\\\": \\\"MDQ6VXNlcjk2MTc0Nw==\\\",\\n+ \\\"organizations_url\\\": \\\"https://api.github.com/users/kszucs/orgs\\\",\\n+ \\\"received_events_url\\\": \\\"https://api.github.com/users/kszucs/received_events\\\",\\n+ \\\"repos_url\\\": \\\"https://api.github.com/users/kszucs/repos\\\",\\n+ \\\"site_admin\\\": false,\\n+ \\\"starred_url\\\": \\\"https://api.github.com/users/kszucs/starred{/owner}{/repo}\\\",\\n+ \\\"subscriptions_url\\\": \\\"https://api.github.com/users/kszucs/subscriptions\\\",\\n+ \\\"type\\\": \\\"User\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/users/kszucs\\\"\\n+ }\\n+ },\\n+ \\\"organization\\\": {\\n+ \\\"avatar_url\\\": \\\"https://avatars2.githubusercontent.com/u/46514972?v=4\\\",\\n+ \\\"description\\\": \\\"Innovation lab for open source data science tools, powered by Apache Arrow\\\",\\n+ \\\"events_url\\\": \\\"https://api.github.com/orgs/ursa-labs/events\\\",\\n+ \\\"hooks_url\\\": \\\"https://api.github.com/orgs/ursa-labs/hooks\\\",\\n+ \\\"id\\\": 46514972,\\n+ \\\"issues_url\\\": \\\"https://api.github.com/orgs/ursa-labs/issues\\\",\\n+ \\\"login\\\": \\\"ursa-labs\\\",\\n+ \\\"members_url\\\": \\\"https://api.github.com/orgs/ursa-labs/members{/member}\\\",\\n+ \\\"node_id\\\": \\\"MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy\\\",\\n+ \\\"public_members_url\\\": \\\"https://api.github.com/orgs/ursa-labs/public_members{/member}\\\",\\n+ \\\"repos_url\\\": \\\"https://api.github.com/orgs/ursa-labs/repos\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/orgs/ursa-labs\\\"\\n+ },\\n+ \\\"repository\\\": {\\n+ \\\"archive_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/{archive_format}{/ref}\\\",\\n+ \\\"archived\\\": false,\\n+ \\\"assignees_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/assignees{/user}\\\",\\n+ \\\"blobs_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/git/blobs{/sha}\\\",\\n+ \\\"branches_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/branches{/branch}\\\",\\n+ \\\"clone_url\\\": \\\"https://github.com/ursa-labs/ursabot.git\\\",\\n+ \\\"collaborators_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/collaborators{/collaborator}\\\",\\n+ \\\"comments_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/comments{/number}\\\",\\n+ \\\"commits_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/commits{/sha}\\\",\\n+ \\\"compare_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/compare/{base}...{head}\\\",\\n+ \\\"contents_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/contents/{+path}\\\",\\n+ \\\"contributors_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/contributors\\\",\\n+ \\\"created_at\\\": \\\"2019-02-04T15:40:31Z\\\",\\n+ \\\"default_branch\\\": \\\"master\\\",\\n+ \\\"deployments_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/deployments\\\",\\n+ \\\"description\\\": null,\\n+ \\\"disabled\\\": false,\\n+ \\\"downloads_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/downloads\\\",\\n+ \\\"events_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/events\\\",\\n+ \\\"fork\\\": false,\\n+ \\\"forks\\\": 0,\\n+ \\\"forks_count\\\": 0,\\n+ \\\"forks_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/forks\\\",\\n+ \\\"full_name\\\": \\\"ursa-labs/ursabot\\\",\\n+ \\\"git_commits_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/git/commits{/sha}\\\",\\n+ \\\"git_refs_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/git/refs{/sha}\\\",\\n+ \\\"git_tags_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/git/tags{/sha}\\\",\\n+ \\\"git_url\\\": \\\"git://github.com/ursa-labs/ursabot.git\\\",\\n+ \\\"has_downloads\\\": true,\\n+ \\\"has_issues\\\": true,\\n+ \\\"has_pages\\\": false,\\n+ \\\"has_projects\\\": true,\\n+ \\\"has_wiki\\\": true,\\n+ \\\"homepage\\\": null,\\n+ \\\"hooks_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/hooks\\\",\\n+ \\\"html_url\\\": \\\"https://github.com/ursa-labs/ursabot\\\",\\n+ \\\"id\\\": 169101701,\\n+ \\\"issue_comment_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues/comments{/number}\\\",\\n+ \\\"issue_events_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues/events{/number}\\\",\\n+ \\\"issues_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues{/number}\\\",\\n+ \\\"keys_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/keys{/key_id}\\\",\\n+ \\\"labels_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/labels{/name}\\\",\\n+ \\\"language\\\": \\\"Jupyter Notebook\\\",\\n+ \\\"languages_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/languages\\\",\\n+ \\\"license\\\": null,\\n+ \\\"merges_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/merges\\\",\\n+ \\\"milestones_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/milestones{/number}\\\",\\n+ \\\"mirror_url\\\": null,\\n+ \\\"name\\\": \\\"ursabot\\\",\\n+ \\\"node_id\\\": \\\"MDEwOlJlcG9zaXRvcnkxNjkxMDE3MDE=\\\",\\n+ \\\"notifications_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/notifications{?since,all,participating}\\\",\\n+ \\\"open_issues\\\": 19,\\n+ \\\"open_issues_count\\\": 19,\\n+ \\\"owner\\\": {\\n+ \\\"avatar_url\\\": \\\"https://avatars2.githubusercontent.com/u/46514972?v=4\\\",\\n+ \\\"events_url\\\": \\\"https://api.github.com/users/ursa-labs/events{/privacy}\\\",\\n+ \\\"followers_url\\\": \\\"https://api.github.com/users/ursa-labs/followers\\\",\\n+ \\\"following_url\\\": \\\"https://api.github.com/users/ursa-labs/following{/other_user}\\\",\\n+ \\\"gists_url\\\": \\\"https://api.github.com/users/ursa-labs/gists{/gist_id}\\\",\\n+ \\\"gravatar_id\\\": \\\"\\\",\\n+ \\\"html_url\\\": \\\"https://github.com/ursa-labs\\\",\\n+ \\\"id\\\": 46514972,\\n+ \\\"login\\\": \\\"ursa-labs\\\",\\n+ \\\"node_id\\\": \\\"MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy\\\",\\n+ \\\"organizations_url\\\": \\\"https://api.github.com/users/ursa-labs/orgs\\\",\\n+ \\\"received_events_url\\\": \\\"https://api.github.com/users/ursa-labs/received_events\\\",\\n+ \\\"repos_url\\\": \\\"https://api.github.com/users/ursa-labs/repos\\\",\\n+ \\\"site_admin\\\": false,\\n+ \\\"starred_url\\\": \\\"https://api.github.com/users/ursa-labs/starred{/owner}{/repo}\\\",\\n+ \\\"subscriptions_url\\\": \\\"https://api.github.com/users/ursa-labs/subscriptions\\\",\\n+ \\\"type\\\": \\\"Organization\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/users/ursa-labs\\\"\\n+ },\\n+ \\\"private\\\": false,\\n+ \\\"pulls_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/pulls{/number}\\\",\\n+ \\\"pushed_at\\\": \\\"2019-04-05T11:22:16Z\\\",\\n+ \\\"releases_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/releases{/id}\\\",\\n+ \\\"size\\\": 892,\\n+ \\\"ssh_url\\\": \\\"git@github.com:ursa-labs/ursabot.git\\\",\\n+ \\\"stargazers_count\\\": 1,\\n+ \\\"stargazers_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/stargazers\\\",\\n+ \\\"statuses_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/statuses/{sha}\\\",\\n+ \\\"subscribers_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/subscribers\\\",\\n+ \\\"subscription_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/subscription\\\",\\n+ \\\"svn_url\\\": \\\"https://github.com/ursa-labs/ursabot\\\",\\n+ \\\"tags_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/tags\\\",\\n+ \\\"teams_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/teams\\\",\\n+ \\\"trees_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/git/trees{/sha}\\\",\\n+ \\\"updated_at\\\": \\\"2019-04-04T17:49:10Z\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot\\\",\\n+ \\\"watchers\\\": 1,\\n+ \\\"watchers_count\\\": 1\\n+ },\\n+ \\\"sender\\\": {\\n+ \\\"avatar_url\\\": \\\"https://avatars1.githubusercontent.com/u/961747?v=4\\\",\\n+ \\\"events_url\\\": \\\"https://api.github.com/users/kszucs/events{/privacy}\\\",\\n+ \\\"followers_url\\\": \\\"https://api.github.com/users/kszucs/followers\\\",\\n+ \\\"following_url\\\": \\\"https://api.github.com/users/kszucs/following{/other_user}\\\",\\n+ \\\"gists_url\\\": \\\"https://api.github.com/users/kszucs/gists{/gist_id}\\\",\\n+ \\\"gravatar_id\\\": \\\"\\\",\\n+ \\\"html_url\\\": \\\"https://github.com/kszucs\\\",\\n+ \\\"id\\\": 961747,\\n+ \\\"login\\\": \\\"kszucs\\\",\\n+ \\\"node_id\\\": \\\"MDQ6VXNlcjk2MTc0Nw==\\\",\\n+ \\\"organizations_url\\\": \\\"https://api.github.com/users/kszucs/orgs\\\",\\n+ \\\"received_events_url\\\": \\\"https://api.github.com/users/kszucs/received_events\\\",\\n+ \\\"repos_url\\\": \\\"https://api.github.com/users/kszucs/repos\\\",\\n+ \\\"site_admin\\\": false,\\n+ \\\"starred_url\\\": \\\"https://api.github.com/users/kszucs/starred{/owner}{/repo}\\\",\\n+ \\\"subscriptions_url\\\": \\\"https://api.github.com/users/kszucs/subscriptions\\\",\\n+ \\\"type\\\": \\\"User\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/users/kszucs\\\"\\n+ }\\n+}\"\n+ },\n+ {\n+ \"sha\": \"2770e29ba9086394455315e590c0b433d08e437e\",\n+ \"filename\": \"ursabot/tests/fixtures/issue-comment-with-empty-command.json\",\n+ \"status\": \"added\",\n+ \"additions\": 212,\n+ \"deletions\": 0,\n+ \"changes\": 212,\n+ \"blob_url\": \"https://github.com/ursa-labs/ursabot/blob/2705da2b616b98fa6010a25813c5a7a27456f71d/ursabot/tests/fixtures/issue-comment-with-empty-command.json\",\n+ \"raw_url\": \"https://github.com/ursa-labs/ursabot/raw/2705da2b616b98fa6010a25813c5a7a27456f71d/ursabot/tests/fixtures/issue-comment-with-empty-command.json\",\n+ \"contents_url\": \"https://api.github.com/repos/ursa-labs/ursabot/contents/ursabot/tests/fixtures/issue-comment-with-empty-command.json?ref=2705da2b616b98fa6010a25813c5a7a27456f71d\",\n+ \"patch\": \"@@ -0,0 +1,212 @@\\n+{\\n+ \\\"action\\\": \\\"created\\\",\\n+ \\\"comment\\\": {\\n+ \\\"author_association\\\": \\\"MEMBER\\\",\\n+ \\\"body\\\": \\\"@ursabot \\\",\\n+ \\\"created_at\\\": \\\"2019-04-05T11:35:46Z\\\",\\n+ \\\"html_url\\\": \\\"https://github.com/ursa-labs/ursabot/pull/26#issuecomment-480243811\\\",\\n+ \\\"id\\\": 480243811,\\n+ \\\"issue_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues/26\\\",\\n+ \\\"node_id\\\": \\\"MDEyOklzc3VlQ29tbWVudDQ4MDI0MzgxMQ==\\\",\\n+ \\\"updated_at\\\": \\\"2019-04-05T11:35:46Z\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues/comments/480243811\\\",\\n+ \\\"user\\\": {\\n+ \\\"avatar_url\\\": \\\"https://avatars1.githubusercontent.com/u/961747?v=4\\\",\\n+ \\\"events_url\\\": \\\"https://api.github.com/users/kszucs/events{/privacy}\\\",\\n+ \\\"followers_url\\\": \\\"https://api.github.com/users/kszucs/followers\\\",\\n+ \\\"following_url\\\": \\\"https://api.github.com/users/kszucs/following{/other_user}\\\",\\n+ \\\"gists_url\\\": \\\"https://api.github.com/users/kszucs/gists{/gist_id}\\\",\\n+ \\\"gravatar_id\\\": \\\"\\\",\\n+ \\\"html_url\\\": \\\"https://github.com/kszucs\\\",\\n+ \\\"id\\\": 961747,\\n+ \\\"login\\\": \\\"kszucs\\\",\\n+ \\\"node_id\\\": \\\"MDQ6VXNlcjk2MTc0Nw==\\\",\\n+ \\\"organizations_url\\\": \\\"https://api.github.com/users/kszucs/orgs\\\",\\n+ \\\"received_events_url\\\": \\\"https://api.github.com/users/kszucs/received_events\\\",\\n+ \\\"repos_url\\\": \\\"https://api.github.com/users/kszucs/repos\\\",\\n+ \\\"site_admin\\\": false,\\n+ \\\"starred_url\\\": \\\"https://api.github.com/users/kszucs/starred{/owner}{/repo}\\\",\\n+ \\\"subscriptions_url\\\": \\\"https://api.github.com/users/kszucs/subscriptions\\\",\\n+ \\\"type\\\": \\\"User\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/users/kszucs\\\"\\n+ }\\n+ },\\n+ \\\"issue\\\": {\\n+ \\\"assignee\\\": null,\\n+ \\\"assignees\\\": [],\\n+ \\\"author_association\\\": \\\"MEMBER\\\",\\n+ \\\"body\\\": \\\"\\\",\\n+ \\\"closed_at\\\": null,\\n+ \\\"comments\\\": 1,\\n+ \\\"comments_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues/26/comments\\\",\\n+ \\\"created_at\\\": \\\"2019-04-05T11:22:15Z\\\",\\n+ \\\"events_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues/26/events\\\",\\n+ \\\"html_url\\\": \\\"https://github.com/ursa-labs/ursabot/pull/26\\\",\\n+ \\\"id\\\": 429706959,\\n+ \\\"labels\\\": [],\\n+ \\\"labels_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues/26/labels{/name}\\\",\\n+ \\\"locked\\\": false,\\n+ \\\"milestone\\\": null,\\n+ \\\"node_id\\\": \\\"MDExOlB1bGxSZXF1ZXN0MjY3Nzg1NTUy\\\",\\n+ \\\"number\\\": 26,\\n+ \\\"pull_request\\\": {\\n+ \\\"diff_url\\\": \\\"https://github.com/ursa-labs/ursabot/pull/26.diff\\\",\\n+ \\\"html_url\\\": \\\"https://github.com/ursa-labs/ursabot/pull/26\\\",\\n+ \\\"patch_url\\\": \\\"https://github.com/ursa-labs/ursabot/pull/26.patch\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/pulls/26\\\"\\n+ },\\n+ \\\"repository_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot\\\",\\n+ \\\"state\\\": \\\"open\\\",\\n+ \\\"title\\\": \\\"Unittests for GithubHook\\\",\\n+ \\\"updated_at\\\": \\\"2019-04-05T11:35:46Z\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues/26\\\",\\n+ \\\"user\\\": {\\n+ \\\"avatar_url\\\": \\\"https://avatars1.githubusercontent.com/u/961747?v=4\\\",\\n+ \\\"events_url\\\": \\\"https://api.github.com/users/kszucs/events{/privacy}\\\",\\n+ \\\"followers_url\\\": \\\"https://api.github.com/users/kszucs/followers\\\",\\n+ \\\"following_url\\\": \\\"https://api.github.com/users/kszucs/following{/other_user}\\\",\\n+ \\\"gists_url\\\": \\\"https://api.github.com/users/kszucs/gists{/gist_id}\\\",\\n+ \\\"gravatar_id\\\": \\\"\\\",\\n+ \\\"html_url\\\": \\\"https://github.com/kszucs\\\",\\n+ \\\"id\\\": 961747,\\n+ \\\"login\\\": \\\"kszucs\\\",\\n+ \\\"node_id\\\": \\\"MDQ6VXNlcjk2MTc0Nw==\\\",\\n+ \\\"organizations_url\\\": \\\"https://api.github.com/users/kszucs/orgs\\\",\\n+ \\\"received_events_url\\\": \\\"https://api.github.com/users/kszucs/received_events\\\",\\n+ \\\"repos_url\\\": \\\"https://api.github.com/users/kszucs/repos\\\",\\n+ \\\"site_admin\\\": false,\\n+ \\\"starred_url\\\": \\\"https://api.github.com/users/kszucs/starred{/owner}{/repo}\\\",\\n+ \\\"subscriptions_url\\\": \\\"https://api.github.com/users/kszucs/subscriptions\\\",\\n+ \\\"type\\\": \\\"User\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/users/kszucs\\\"\\n+ }\\n+ },\\n+ \\\"organization\\\": {\\n+ \\\"avatar_url\\\": \\\"https://avatars2.githubusercontent.com/u/46514972?v=4\\\",\\n+ \\\"description\\\": \\\"Innovation lab for open source data science tools, powered by Apache Arrow\\\",\\n+ \\\"events_url\\\": \\\"https://api.github.com/orgs/ursa-labs/events\\\",\\n+ \\\"hooks_url\\\": \\\"https://api.github.com/orgs/ursa-labs/hooks\\\",\\n+ \\\"id\\\": 46514972,\\n+ \\\"issues_url\\\": \\\"https://api.github.com/orgs/ursa-labs/issues\\\",\\n+ \\\"login\\\": \\\"ursa-labs\\\",\\n+ \\\"members_url\\\": \\\"https://api.github.com/orgs/ursa-labs/members{/member}\\\",\\n+ \\\"node_id\\\": \\\"MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy\\\",\\n+ \\\"public_members_url\\\": \\\"https://api.github.com/orgs/ursa-labs/public_members{/member}\\\",\\n+ \\\"repos_url\\\": \\\"https://api.github.com/orgs/ursa-labs/repos\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/orgs/ursa-labs\\\"\\n+ },\\n+ \\\"repository\\\": {\\n+ \\\"archive_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/{archive_format}{/ref}\\\",\\n+ \\\"archived\\\": false,\\n+ \\\"assignees_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/assignees{/user}\\\",\\n+ \\\"blobs_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/git/blobs{/sha}\\\",\\n+ \\\"branches_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/branches{/branch}\\\",\\n+ \\\"clone_url\\\": \\\"https://github.com/ursa-labs/ursabot.git\\\",\\n+ \\\"collaborators_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/collaborators{/collaborator}\\\",\\n+ \\\"comments_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/comments{/number}\\\",\\n+ \\\"commits_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/commits{/sha}\\\",\\n+ \\\"compare_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/compare/{base}...{head}\\\",\\n+ \\\"contents_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/contents/{+path}\\\",\\n+ \\\"contributors_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/contributors\\\",\\n+ \\\"created_at\\\": \\\"2019-02-04T15:40:31Z\\\",\\n+ \\\"default_branch\\\": \\\"master\\\",\\n+ \\\"deployments_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/deployments\\\",\\n+ \\\"description\\\": null,\\n+ \\\"disabled\\\": false,\\n+ \\\"downloads_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/downloads\\\",\\n+ \\\"events_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/events\\\",\\n+ \\\"fork\\\": false,\\n+ \\\"forks\\\": 0,\\n+ \\\"forks_count\\\": 0,\\n+ \\\"forks_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/forks\\\",\\n+ \\\"full_name\\\": \\\"ursa-labs/ursabot\\\",\\n+ \\\"git_commits_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/git/commits{/sha}\\\",\\n+ \\\"git_refs_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/git/refs{/sha}\\\",\\n+ \\\"git_tags_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/git/tags{/sha}\\\",\\n+ \\\"git_url\\\": \\\"git://github.com/ursa-labs/ursabot.git\\\",\\n+ \\\"has_downloads\\\": true,\\n+ \\\"has_issues\\\": true,\\n+ \\\"has_pages\\\": false,\\n+ \\\"has_projects\\\": true,\\n+ \\\"has_wiki\\\": true,\\n+ \\\"homepage\\\": null,\\n+ \\\"hooks_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/hooks\\\",\\n+ \\\"html_url\\\": \\\"https://github.com/ursa-labs/ursabot\\\",\\n+ \\\"id\\\": 169101701,\\n+ \\\"issue_comment_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues/comments{/number}\\\",\\n+ \\\"issue_events_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues/events{/number}\\\",\\n+ \\\"issues_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues{/number}\\\",\\n+ \\\"keys_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/keys{/key_id}\\\",\\n+ \\\"labels_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/labels{/name}\\\",\\n+ \\\"language\\\": \\\"Jupyter Notebook\\\",\\n+ \\\"languages_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/languages\\\",\\n+ \\\"license\\\": null,\\n+ \\\"merges_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/merges\\\",\\n+ \\\"milestones_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/milestones{/number}\\\",\\n+ \\\"mirror_url\\\": null,\\n+ \\\"name\\\": \\\"ursabot\\\",\\n+ \\\"node_id\\\": \\\"MDEwOlJlcG9zaXRvcnkxNjkxMDE3MDE=\\\",\\n+ \\\"notifications_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/notifications{?since,all,participating}\\\",\\n+ \\\"open_issues\\\": 19,\\n+ \\\"open_issues_count\\\": 19,\\n+ \\\"owner\\\": {\\n+ \\\"avatar_url\\\": \\\"https://avatars2.githubusercontent.com/u/46514972?v=4\\\",\\n+ \\\"events_url\\\": \\\"https://api.github.com/users/ursa-labs/events{/privacy}\\\",\\n+ \\\"followers_url\\\": \\\"https://api.github.com/users/ursa-labs/followers\\\",\\n+ \\\"following_url\\\": \\\"https://api.github.com/users/ursa-labs/following{/other_user}\\\",\\n+ \\\"gists_url\\\": \\\"https://api.github.com/users/ursa-labs/gists{/gist_id}\\\",\\n+ \\\"gravatar_id\\\": \\\"\\\",\\n+ \\\"html_url\\\": \\\"https://github.com/ursa-labs\\\",\\n+ \\\"id\\\": 46514972,\\n+ \\\"login\\\": \\\"ursa-labs\\\",\\n+ \\\"node_id\\\": \\\"MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy\\\",\\n+ \\\"organizations_url\\\": \\\"https://api.github.com/users/ursa-labs/orgs\\\",\\n+ \\\"received_events_url\\\": \\\"https://api.github.com/users/ursa-labs/received_events\\\",\\n+ \\\"repos_url\\\": \\\"https://api.github.com/users/ursa-labs/repos\\\",\\n+ \\\"site_admin\\\": false,\\n+ \\\"starred_url\\\": \\\"https://api.github.com/users/ursa-labs/starred{/owner}{/repo}\\\",\\n+ \\\"subscriptions_url\\\": \\\"https://api.github.com/users/ursa-labs/subscriptions\\\",\\n+ \\\"type\\\": \\\"Organization\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/users/ursa-labs\\\"\\n+ },\\n+ \\\"private\\\": false,\\n+ \\\"pulls_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/pulls{/number}\\\",\\n+ \\\"pushed_at\\\": \\\"2019-04-05T11:22:16Z\\\",\\n+ \\\"releases_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/releases{/id}\\\",\\n+ \\\"size\\\": 892,\\n+ \\\"ssh_url\\\": \\\"git@github.com:ursa-labs/ursabot.git\\\",\\n+ \\\"stargazers_count\\\": 1,\\n+ \\\"stargazers_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/stargazers\\\",\\n+ \\\"statuses_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/statuses/{sha}\\\",\\n+ \\\"subscribers_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/subscribers\\\",\\n+ \\\"subscription_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/subscription\\\",\\n+ \\\"svn_url\\\": \\\"https://github.com/ursa-labs/ursabot\\\",\\n+ \\\"tags_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/tags\\\",\\n+ \\\"teams_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/teams\\\",\\n+ \\\"trees_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/git/trees{/sha}\\\",\\n+ \\\"updated_at\\\": \\\"2019-04-04T17:49:10Z\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot\\\",\\n+ \\\"watchers\\\": 1,\\n+ \\\"watchers_count\\\": 1\\n+ },\\n+ \\\"sender\\\": {\\n+ \\\"avatar_url\\\": \\\"https://avatars1.githubusercontent.com/u/961747?v=4\\\",\\n+ \\\"events_url\\\": \\\"https://api.github.com/users/kszucs/events{/privacy}\\\",\\n+ \\\"followers_url\\\": \\\"https://api.github.com/users/kszucs/followers\\\",\\n+ \\\"following_url\\\": \\\"https://api.github.com/users/kszucs/following{/other_user}\\\",\\n+ \\\"gists_url\\\": \\\"https://api.github.com/users/kszucs/gists{/gist_id}\\\",\\n+ \\\"gravatar_id\\\": \\\"\\\",\\n+ \\\"html_url\\\": \\\"https://github.com/kszucs\\\",\\n+ \\\"id\\\": 961747,\\n+ \\\"login\\\": \\\"kszucs\\\",\\n+ \\\"node_id\\\": \\\"MDQ6VXNlcjk2MTc0Nw==\\\",\\n+ \\\"organizations_url\\\": \\\"https://api.github.com/users/kszucs/orgs\\\",\\n+ \\\"received_events_url\\\": \\\"https://api.github.com/users/kszucs/received_events\\\",\\n+ \\\"repos_url\\\": \\\"https://api.github.com/users/kszucs/repos\\\",\\n+ \\\"site_admin\\\": false,\\n+ \\\"starred_url\\\": \\\"https://api.github.com/users/kszucs/starred{/owner}{/repo}\\\",\\n+ \\\"subscriptions_url\\\": \\\"https://api.github.com/users/kszucs/subscriptions\\\",\\n+ \\\"type\\\": \\\"User\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/users/kszucs\\\"\\n+ }\\n+}\"\n+ },\n+ {\n+ \"sha\": \"80ff46510a2f39ae60f7c3a98e5fdaef8e688784\",\n+ \"filename\": \"ursabot/tests/fixtures/issue-comment-without-pull-request.json\",\n+ \"status\": \"added\",\n+ \"additions\": 206,\n+ \"deletions\": 0,\n+ \"changes\": 206,\n+ \"blob_url\": \"https://github.com/ursa-labs/ursabot/blob/2705da2b616b98fa6010a25813c5a7a27456f71d/ursabot/tests/fixtures/issue-comment-without-pull-request.json\",\n+ \"raw_url\": \"https://github.com/ursa-labs/ursabot/raw/2705da2b616b98fa6010a25813c5a7a27456f71d/ursabot/tests/fixtures/issue-comment-without-pull-request.json\",\n+ \"contents_url\": \"https://api.github.com/repos/ursa-labs/ursabot/contents/ursabot/tests/fixtures/issue-comment-without-pull-request.json?ref=2705da2b616b98fa6010a25813c5a7a27456f71d\",\n+ \"patch\": \"@@ -0,0 +1,206 @@\\n+{\\n+ \\\"action\\\": \\\"created\\\",\\n+ \\\"comment\\\": {\\n+ \\\"author_association\\\": \\\"NONE\\\",\\n+ \\\"body\\\": \\\"Ursabot only listens to pull request comments!\\\",\\n+ \\\"created_at\\\": \\\"2019-04-05T11:53:43Z\\\",\\n+ \\\"html_url\\\": \\\"https://github.com/ursa-labs/ursabot/issues/19#issuecomment-480248217\\\",\\n+ \\\"id\\\": 480248217,\\n+ \\\"issue_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues/19\\\",\\n+ \\\"node_id\\\": \\\"MDEyOklzc3VlQ29tbWVudDQ4MDI0ODIxNw==\\\",\\n+ \\\"updated_at\\\": \\\"2019-04-05T11:53:43Z\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues/comments/480248217\\\",\\n+ \\\"user\\\": {\\n+ \\\"avatar_url\\\": \\\"https://avatars2.githubusercontent.com/u/49275095?v=4\\\",\\n+ \\\"events_url\\\": \\\"https://api.github.com/users/ursabot/events{/privacy}\\\",\\n+ \\\"followers_url\\\": \\\"https://api.github.com/users/ursabot/followers\\\",\\n+ \\\"following_url\\\": \\\"https://api.github.com/users/ursabot/following{/other_user}\\\",\\n+ \\\"gists_url\\\": \\\"https://api.github.com/users/ursabot/gists{/gist_id}\\\",\\n+ \\\"gravatar_id\\\": \\\"\\\",\\n+ \\\"html_url\\\": \\\"https://github.com/ursabot\\\",\\n+ \\\"id\\\": 49275095,\\n+ \\\"login\\\": \\\"ursabot\\\",\\n+ \\\"node_id\\\": \\\"MDQ6VXNlcjQ5Mjc1MDk1\\\",\\n+ \\\"organizations_url\\\": \\\"https://api.github.com/users/ursabot/orgs\\\",\\n+ \\\"received_events_url\\\": \\\"https://api.github.com/users/ursabot/received_events\\\",\\n+ \\\"repos_url\\\": \\\"https://api.github.com/users/ursabot/repos\\\",\\n+ \\\"site_admin\\\": false,\\n+ \\\"starred_url\\\": \\\"https://api.github.com/users/ursabot/starred{/owner}{/repo}\\\",\\n+ \\\"subscriptions_url\\\": \\\"https://api.github.com/users/ursabot/subscriptions\\\",\\n+ \\\"type\\\": \\\"User\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/users/ursabot\\\"\\n+ }\\n+ },\\n+ \\\"issue\\\": {\\n+ \\\"assignee\\\": null,\\n+ \\\"assignees\\\": [],\\n+ \\\"author_association\\\": \\\"MEMBER\\\",\\n+ \\\"body\\\": \\\"\\\",\\n+ \\\"closed_at\\\": null,\\n+ \\\"comments\\\": 4,\\n+ \\\"comments_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues/19/comments\\\",\\n+ \\\"created_at\\\": \\\"2019-04-02T09:56:41Z\\\",\\n+ \\\"events_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues/19/events\\\",\\n+ \\\"html_url\\\": \\\"https://github.com/ursa-labs/ursabot/issues/19\\\",\\n+ \\\"id\\\": 428131685,\\n+ \\\"labels\\\": [],\\n+ \\\"labels_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues/19/labels{/name}\\\",\\n+ \\\"locked\\\": false,\\n+ \\\"milestone\\\": null,\\n+ \\\"node_id\\\": \\\"MDU6SXNzdWU0MjgxMzE2ODU=\\\",\\n+ \\\"number\\\": 19,\\n+ \\\"repository_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot\\\",\\n+ \\\"state\\\": \\\"open\\\",\\n+ \\\"title\\\": \\\"Build ursabot itself via ursabot\\\",\\n+ \\\"updated_at\\\": \\\"2019-04-05T11:53:43Z\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues/19\\\",\\n+ \\\"user\\\": {\\n+ \\\"avatar_url\\\": \\\"https://avatars1.githubusercontent.com/u/961747?v=4\\\",\\n+ \\\"events_url\\\": \\\"https://api.github.com/users/kszucs/events{/privacy}\\\",\\n+ \\\"followers_url\\\": \\\"https://api.github.com/users/kszucs/followers\\\",\\n+ \\\"following_url\\\": \\\"https://api.github.com/users/kszucs/following{/other_user}\\\",\\n+ \\\"gists_url\\\": \\\"https://api.github.com/users/kszucs/gists{/gist_id}\\\",\\n+ \\\"gravatar_id\\\": \\\"\\\",\\n+ \\\"html_url\\\": \\\"https://github.com/kszucs\\\",\\n+ \\\"id\\\": 961747,\\n+ \\\"login\\\": \\\"kszucs\\\",\\n+ \\\"node_id\\\": \\\"MDQ6VXNlcjk2MTc0Nw==\\\",\\n+ \\\"organizations_url\\\": \\\"https://api.github.com/users/kszucs/orgs\\\",\\n+ \\\"received_events_url\\\": \\\"https://api.github.com/users/kszucs/received_events\\\",\\n+ \\\"repos_url\\\": \\\"https://api.github.com/users/kszucs/repos\\\",\\n+ \\\"site_admin\\\": false,\\n+ \\\"starred_url\\\": \\\"https://api.github.com/users/kszucs/starred{/owner}{/repo}\\\",\\n+ \\\"subscriptions_url\\\": \\\"https://api.github.com/users/kszucs/subscriptions\\\",\\n+ \\\"type\\\": \\\"User\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/users/kszucs\\\"\\n+ }\\n+ },\\n+ \\\"organization\\\": {\\n+ \\\"avatar_url\\\": \\\"https://avatars2.githubusercontent.com/u/46514972?v=4\\\",\\n+ \\\"description\\\": \\\"Innovation lab for open source data science tools, powered by Apache Arrow\\\",\\n+ \\\"events_url\\\": \\\"https://api.github.com/orgs/ursa-labs/events\\\",\\n+ \\\"hooks_url\\\": \\\"https://api.github.com/orgs/ursa-labs/hooks\\\",\\n+ \\\"id\\\": 46514972,\\n+ \\\"issues_url\\\": \\\"https://api.github.com/orgs/ursa-labs/issues\\\",\\n+ \\\"login\\\": \\\"ursa-labs\\\",\\n+ \\\"members_url\\\": \\\"https://api.github.com/orgs/ursa-labs/members{/member}\\\",\\n+ \\\"node_id\\\": \\\"MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy\\\",\\n+ \\\"public_members_url\\\": \\\"https://api.github.com/orgs/ursa-labs/public_members{/member}\\\",\\n+ \\\"repos_url\\\": \\\"https://api.github.com/orgs/ursa-labs/repos\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/orgs/ursa-labs\\\"\\n+ },\\n+ \\\"repository\\\": {\\n+ \\\"archive_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/{archive_format}{/ref}\\\",\\n+ \\\"archived\\\": false,\\n+ \\\"assignees_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/assignees{/user}\\\",\\n+ \\\"blobs_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/git/blobs{/sha}\\\",\\n+ \\\"branches_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/branches{/branch}\\\",\\n+ \\\"clone_url\\\": \\\"https://github.com/ursa-labs/ursabot.git\\\",\\n+ \\\"collaborators_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/collaborators{/collaborator}\\\",\\n+ \\\"comments_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/comments{/number}\\\",\\n+ \\\"commits_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/commits{/sha}\\\",\\n+ \\\"compare_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/compare/{base}...{head}\\\",\\n+ \\\"contents_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/contents/{+path}\\\",\\n+ \\\"contributors_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/contributors\\\",\\n+ \\\"created_at\\\": \\\"2019-02-04T15:40:31Z\\\",\\n+ \\\"default_branch\\\": \\\"master\\\",\\n+ \\\"deployments_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/deployments\\\",\\n+ \\\"description\\\": null,\\n+ \\\"disabled\\\": false,\\n+ \\\"downloads_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/downloads\\\",\\n+ \\\"events_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/events\\\",\\n+ \\\"fork\\\": false,\\n+ \\\"forks\\\": 0,\\n+ \\\"forks_count\\\": 0,\\n+ \\\"forks_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/forks\\\",\\n+ \\\"full_name\\\": \\\"ursa-labs/ursabot\\\",\\n+ \\\"git_commits_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/git/commits{/sha}\\\",\\n+ \\\"git_refs_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/git/refs{/sha}\\\",\\n+ \\\"git_tags_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/git/tags{/sha}\\\",\\n+ \\\"git_url\\\": \\\"git://github.com/ursa-labs/ursabot.git\\\",\\n+ \\\"has_downloads\\\": true,\\n+ \\\"has_issues\\\": true,\\n+ \\\"has_pages\\\": false,\\n+ \\\"has_projects\\\": true,\\n+ \\\"has_wiki\\\": true,\\n+ \\\"homepage\\\": null,\\n+ \\\"hooks_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/hooks\\\",\\n+ \\\"html_url\\\": \\\"https://github.com/ursa-labs/ursabot\\\",\\n+ \\\"id\\\": 169101701,\\n+ \\\"issue_comment_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues/comments{/number}\\\",\\n+ \\\"issue_events_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues/events{/number}\\\",\\n+ \\\"issues_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/issues{/number}\\\",\\n+ \\\"keys_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/keys{/key_id}\\\",\\n+ \\\"labels_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/labels{/name}\\\",\\n+ \\\"language\\\": \\\"Jupyter Notebook\\\",\\n+ \\\"languages_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/languages\\\",\\n+ \\\"license\\\": null,\\n+ \\\"merges_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/merges\\\",\\n+ \\\"milestones_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/milestones{/number}\\\",\\n+ \\\"mirror_url\\\": null,\\n+ \\\"name\\\": \\\"ursabot\\\",\\n+ \\\"node_id\\\": \\\"MDEwOlJlcG9zaXRvcnkxNjkxMDE3MDE=\\\",\\n+ \\\"notifications_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/notifications{?since,all,participating}\\\",\\n+ \\\"open_issues\\\": 19,\\n+ \\\"open_issues_count\\\": 19,\\n+ \\\"owner\\\": {\\n+ \\\"avatar_url\\\": \\\"https://avatars2.githubusercontent.com/u/46514972?v=4\\\",\\n+ \\\"events_url\\\": \\\"https://api.github.com/users/ursa-labs/events{/privacy}\\\",\\n+ \\\"followers_url\\\": \\\"https://api.github.com/users/ursa-labs/followers\\\",\\n+ \\\"following_url\\\": \\\"https://api.github.com/users/ursa-labs/following{/other_user}\\\",\\n+ \\\"gists_url\\\": \\\"https://api.github.com/users/ursa-labs/gists{/gist_id}\\\",\\n+ \\\"gravatar_id\\\": \\\"\\\",\\n+ \\\"html_url\\\": \\\"https://github.com/ursa-labs\\\",\\n+ \\\"id\\\": 46514972,\\n+ \\\"login\\\": \\\"ursa-labs\\\",\\n+ \\\"node_id\\\": \\\"MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy\\\",\\n+ \\\"organizations_url\\\": \\\"https://api.github.com/users/ursa-labs/orgs\\\",\\n+ \\\"received_events_url\\\": \\\"https://api.github.com/users/ursa-labs/received_events\\\",\\n+ \\\"repos_url\\\": \\\"https://api.github.com/users/ursa-labs/repos\\\",\\n+ \\\"site_admin\\\": false,\\n+ \\\"starred_url\\\": \\\"https://api.github.com/users/ursa-labs/starred{/owner}{/repo}\\\",\\n+ \\\"subscriptions_url\\\": \\\"https://api.github.com/users/ursa-labs/subscriptions\\\",\\n+ \\\"type\\\": \\\"Organization\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/users/ursa-labs\\\"\\n+ },\\n+ \\\"private\\\": false,\\n+ \\\"pulls_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/pulls{/number}\\\",\\n+ \\\"pushed_at\\\": \\\"2019-04-05T11:22:16Z\\\",\\n+ \\\"releases_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/releases{/id}\\\",\\n+ \\\"size\\\": 892,\\n+ \\\"ssh_url\\\": \\\"git@github.com:ursa-labs/ursabot.git\\\",\\n+ \\\"stargazers_count\\\": 1,\\n+ \\\"stargazers_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/stargazers\\\",\\n+ \\\"statuses_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/statuses/{sha}\\\",\\n+ \\\"subscribers_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/subscribers\\\",\\n+ \\\"subscription_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/subscription\\\",\\n+ \\\"svn_url\\\": \\\"https://github.com/ursa-labs/ursabot\\\",\\n+ \\\"tags_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/tags\\\",\\n+ \\\"teams_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/teams\\\",\\n+ \\\"trees_url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot/git/trees{/sha}\\\",\\n+ \\\"updated_at\\\": \\\"2019-04-04T17:49:10Z\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/repos/ursa-labs/ursabot\\\",\\n+ \\\"watchers\\\": 1,\\n+ \\\"watchers_count\\\": 1\\n+ },\\n+ \\\"sender\\\": {\\n+ \\\"avatar_url\\\": \\\"https://avatars2.githubusercontent.com/u/49275095?v=4\\\",\\n+ \\\"events_url\\\": \\\"https://api.github.com/users/ursabot/events{/privacy}\\\",\\n+ \\\"followers_url\\\": \\\"https://api.github.com/users/ursabot/followers\\\",\\n+ \\\"following_url\\\": \\\"https://api.github.com/users/ursabot/following{/other_user}\\\",\\n+ \\\"gists_url\\\": \\\"https://api.github.com/users/ursabot/gists{/gist_id}\\\",\\n+ \\\"gravatar_id\\\": \\\"\\\",\\n+ \\\"html_url\\\": \\\"https://github.com/ursabot\\\",\\n+ \\\"id\\\": 49275095,\\n+ \\\"login\\\": \\\"ursabot\\\",\\n+ \\\"node_id\\\": \\\"MDQ6VXNlcjQ5Mjc1MDk1\\\",\\n+ \\\"organizations_url\\\": \\\"https://api.github.com/users/ursabot/orgs\\\",\\n+ \\\"received_events_url\\\": \\\"https://api.github.com/users/ursabot/received_events\\\",\\n+ \\\"repos_url\\\": \\\"https://api.github.com/users/ursabot/repos\\\",\\n+ \\\"site_admin\\\": false,\\n+ \\\"starred_url\\\": \\\"https://api.github.com/users/ursabot/starred{/owner}{/repo}\\\",\\n+ \\\"subscriptions_url\\\": \\\"https://api.github.com/users/ursabot/subscriptions\\\",\\n+ \\\"type\\\": \\\"User\\\",\\n+ \\\"url\\\": \\\"https://api.github.com/users/ursabot\\\"\\n+ }\\n+}\"\n+ },\n+ {\n+ \"sha\": \"c738bb0eb54c87ba0f23e97e827d77c2be74d0b6\",\n+ \"filename\": \"ursabot/tests/test_hooks.py\",\n+ \"status\": \"modified\",\n+ \"additions\": 4,\n+ \"deletions\": 4,\n+ \"changes\": 8,\n+ \"blob_url\": \"https://github.com/ursa-labs/ursabot/blob/2705da2b616b98fa6010a25813c5a7a27456f71d/ursabot/tests/test_hooks.py\",\n+ \"raw_url\": \"https://github.com/ursa-labs/ursabot/raw/2705da2b616b98fa6010a25813c5a7a27456f71d/ursabot/tests/test_hooks.py\",\n+ \"contents_url\": \"https://api.github.com/repos/ursa-labs/ursabot/contents/ursabot/tests/test_hooks.py?ref=2705da2b616b98fa6010a25813c5a7a27456f71d\",\n+ \"patch\": \"@@ -54,7 +54,7 @@ class TestGithubHook(ChangeHookTestCase):\\n await self.request('ping', {})\\n assert len(self.hook.master.data.updates.changesAdded) == 0\\n \\n- @ensure_deferred\\n- async def test_issue_comment(self):\\n- payload = {}\\n- await self.request('issue_comment', payload)\\n+ # @ensure_deferred\\n+ # async def test_issue_comment(self):\\n+ # payload = {}\\n+ # await self.request('issue_comment', payload)\"\n+ }\n+ ]\n+}"
- },
- {
- "sha": "ad061d7244b917e6ea3853698dc3bc2a8c9c6857",
- "filename": "ursabot/tests/fixtures/pull-request-26.json",
- "status": "added",
- "additions": 335,
- "deletions": 0,
- "changes": 335,
- "blob_url": "https://github.com/ursa-labs/ursabot/blob/70267dee34884e4b972388e1b30d57f6248c58d0/ursabot/tests/fixtures/pull-request-26.json",
- "raw_url": "https://github.com/ursa-labs/ursabot/raw/70267dee34884e4b972388e1b30d57f6248c58d0/ursabot/tests/fixtures/pull-request-26.json",
- "contents_url": "https://api.github.com/repos/ursa-labs/ursabot/contents/ursabot/tests/fixtures/pull-request-26.json?ref=70267dee34884e4b972388e1b30d57f6248c58d0",
- "patch": "@@ -0,0 +1,335 @@\n+{\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot/pulls/26\",\n+ \"id\": 267785552,\n+ \"node_id\": \"MDExOlB1bGxSZXF1ZXN0MjY3Nzg1NTUy\",\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot/pull/26\",\n+ \"diff_url\": \"https://github.com/ursa-labs/ursabot/pull/26.diff\",\n+ \"patch_url\": \"https://github.com/ursa-labs/ursabot/pull/26.patch\",\n+ \"issue_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26\",\n+ \"number\": 26,\n+ \"state\": \"open\",\n+ \"locked\": false,\n+ \"title\": \"Unittests for GithubHook\",\n+ \"user\": {\n+ \"login\": \"kszucs\",\n+ \"id\": 961747,\n+ \"node_id\": \"MDQ6VXNlcjk2MTc0Nw==\",\n+ \"avatar_url\": \"https://avatars1.githubusercontent.com/u/961747?v=4\",\n+ \"gravatar_id\": \"\",\n+ \"url\": \"https://api.github.com/users/kszucs\",\n+ \"html_url\": \"https://github.com/kszucs\",\n+ \"followers_url\": \"https://api.github.com/users/kszucs/followers\",\n+ \"following_url\": \"https://api.github.com/users/kszucs/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/kszucs/gists{/gist_id}\",\n+ \"starred_url\": \"https://api.github.com/users/kszucs/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/kszucs/subscriptions\",\n+ \"organizations_url\": \"https://api.github.com/users/kszucs/orgs\",\n+ \"repos_url\": \"https://api.github.com/users/kszucs/repos\",\n+ \"events_url\": \"https://api.github.com/users/kszucs/events{/privacy}\",\n+ \"received_events_url\": \"https://api.github.com/users/kszucs/received_events\",\n+ \"type\": \"User\",\n+ \"site_admin\": false\n+ },\n+ \"body\": \"\",\n+ \"created_at\": \"2019-04-05T11:22:15Z\",\n+ \"updated_at\": \"2019-04-05T12:01:40Z\",\n+ \"closed_at\": null,\n+ \"merged_at\": null,\n+ \"merge_commit_sha\": \"cc5dc3606988b3824be54df779ed2028776113cb\",\n+ \"assignee\": null,\n+ \"assignees\": [\n+\n+ ],\n+ \"requested_reviewers\": [\n+\n+ ],\n+ \"requested_teams\": [\n+\n+ ],\n+ \"labels\": [\n+\n+ ],\n+ \"milestone\": null,\n+ \"commits_url\": \"https://api.github.com/repos/ursa-labs/ursabot/pulls/26/commits\",\n+ \"review_comments_url\": \"https://api.github.com/repos/ursa-labs/ursabot/pulls/26/comments\",\n+ \"review_comment_url\": \"https://api.github.com/repos/ursa-labs/ursabot/pulls/comments{/number}\",\n+ \"comments_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26/comments\",\n+ \"statuses_url\": \"https://api.github.com/repos/ursa-labs/ursabot/statuses/2705da2b616b98fa6010a25813c5a7a27456f71d\",\n+ \"head\": {\n+ \"label\": \"ursa-labs:test-hook\",\n+ \"ref\": \"test-hook\",\n+ \"sha\": \"2705da2b616b98fa6010a25813c5a7a27456f71d\",\n+ \"user\": {\n+ \"login\": \"ursa-labs\",\n+ \"id\": 46514972,\n+ \"node_id\": \"MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy\",\n+ \"avatar_url\": \"https://avatars2.githubusercontent.com/u/46514972?v=4\",\n+ \"gravatar_id\": \"\",\n+ \"url\": \"https://api.github.com/users/ursa-labs\",\n+ \"html_url\": \"https://github.com/ursa-labs\",\n+ \"followers_url\": \"https://api.github.com/users/ursa-labs/followers\",\n+ \"following_url\": \"https://api.github.com/users/ursa-labs/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/ursa-labs/gists{/gist_id}\",\n+ \"starred_url\": \"https://api.github.com/users/ursa-labs/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/ursa-labs/subscriptions\",\n+ \"organizations_url\": \"https://api.github.com/users/ursa-labs/orgs\",\n+ \"repos_url\": \"https://api.github.com/users/ursa-labs/repos\",\n+ \"events_url\": \"https://api.github.com/users/ursa-labs/events{/privacy}\",\n+ \"received_events_url\": \"https://api.github.com/users/ursa-labs/received_events\",\n+ \"type\": \"Organization\",\n+ \"site_admin\": false\n+ },\n+ \"repo\": {\n+ \"id\": 169101701,\n+ \"node_id\": \"MDEwOlJlcG9zaXRvcnkxNjkxMDE3MDE=\",\n+ \"name\": \"ursabot\",\n+ \"full_name\": \"ursa-labs/ursabot\",\n+ \"private\": false,\n+ \"owner\": {\n+ \"login\": \"ursa-labs\",\n+ \"id\": 46514972,\n+ \"node_id\": \"MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy\",\n+ \"avatar_url\": \"https://avatars2.githubusercontent.com/u/46514972?v=4\",\n+ \"gravatar_id\": \"\",\n+ \"url\": \"https://api.github.com/users/ursa-labs\",\n+ \"html_url\": \"https://github.com/ursa-labs\",\n+ \"followers_url\": \"https://api.github.com/users/ursa-labs/followers\",\n+ \"following_url\": \"https://api.github.com/users/ursa-labs/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/ursa-labs/gists{/gist_id}\",\n+ \"starred_url\": \"https://api.github.com/users/ursa-labs/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/ursa-labs/subscriptions\",\n+ \"organizations_url\": \"https://api.github.com/users/ursa-labs/orgs\",\n+ \"repos_url\": \"https://api.github.com/users/ursa-labs/repos\",\n+ \"events_url\": \"https://api.github.com/users/ursa-labs/events{/privacy}\",\n+ \"received_events_url\": \"https://api.github.com/users/ursa-labs/received_events\",\n+ \"type\": \"Organization\",\n+ \"site_admin\": false\n+ },\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot\",\n+ \"description\": null,\n+ \"fork\": false,\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot\",\n+ \"forks_url\": \"https://api.github.com/repos/ursa-labs/ursabot/forks\",\n+ \"keys_url\": \"https://api.github.com/repos/ursa-labs/ursabot/keys{/key_id}\",\n+ \"collaborators_url\": \"https://api.github.com/repos/ursa-labs/ursabot/collaborators{/collaborator}\",\n+ \"teams_url\": \"https://api.github.com/repos/ursa-labs/ursabot/teams\",\n+ \"hooks_url\": \"https://api.github.com/repos/ursa-labs/ursabot/hooks\",\n+ \"issue_events_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/events{/number}\",\n+ \"events_url\": \"https://api.github.com/repos/ursa-labs/ursabot/events\",\n+ \"assignees_url\": \"https://api.github.com/repos/ursa-labs/ursabot/assignees{/user}\",\n+ \"branches_url\": \"https://api.github.com/repos/ursa-labs/ursabot/branches{/branch}\",\n+ \"tags_url\": \"https://api.github.com/repos/ursa-labs/ursabot/tags\",\n+ \"blobs_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/blobs{/sha}\",\n+ \"git_tags_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/tags{/sha}\",\n+ \"git_refs_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/refs{/sha}\",\n+ \"trees_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/trees{/sha}\",\n+ \"statuses_url\": \"https://api.github.com/repos/ursa-labs/ursabot/statuses/{sha}\",\n+ \"languages_url\": \"https://api.github.com/repos/ursa-labs/ursabot/languages\",\n+ \"stargazers_url\": \"https://api.github.com/repos/ursa-labs/ursabot/stargazers\",\n+ \"contributors_url\": \"https://api.github.com/repos/ursa-labs/ursabot/contributors\",\n+ \"subscribers_url\": \"https://api.github.com/repos/ursa-labs/ursabot/subscribers\",\n+ \"subscription_url\": \"https://api.github.com/repos/ursa-labs/ursabot/subscription\",\n+ \"commits_url\": \"https://api.github.com/repos/ursa-labs/ursabot/commits{/sha}\",\n+ \"git_commits_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/commits{/sha}\",\n+ \"comments_url\": \"https://api.github.com/repos/ursa-labs/ursabot/comments{/number}\",\n+ \"issue_comment_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/comments{/number}\",\n+ \"contents_url\": \"https://api.github.com/repos/ursa-labs/ursabot/contents/{+path}\",\n+ \"compare_url\": \"https://api.github.com/repos/ursa-labs/ursabot/compare/{base}...{head}\",\n+ \"merges_url\": \"https://api.github.com/repos/ursa-labs/ursabot/merges\",\n+ \"archive_url\": \"https://api.github.com/repos/ursa-labs/ursabot/{archive_format}{/ref}\",\n+ \"downloads_url\": \"https://api.github.com/repos/ursa-labs/ursabot/downloads\",\n+ \"issues_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues{/number}\",\n+ \"pulls_url\": \"https://api.github.com/repos/ursa-labs/ursabot/pulls{/number}\",\n+ \"milestones_url\": \"https://api.github.com/repos/ursa-labs/ursabot/milestones{/number}\",\n+ \"notifications_url\": \"https://api.github.com/repos/ursa-labs/ursabot/notifications{?since,all,participating}\",\n+ \"labels_url\": \"https://api.github.com/repos/ursa-labs/ursabot/labels{/name}\",\n+ \"releases_url\": \"https://api.github.com/repos/ursa-labs/ursabot/releases{/id}\",\n+ \"deployments_url\": \"https://api.github.com/repos/ursa-labs/ursabot/deployments\",\n+ \"created_at\": \"2019-02-04T15:40:31Z\",\n+ \"updated_at\": \"2019-04-04T17:49:10Z\",\n+ \"pushed_at\": \"2019-04-05T12:01:40Z\",\n+ \"git_url\": \"git://github.com/ursa-labs/ursabot.git\",\n+ \"ssh_url\": \"git@github.com:ursa-labs/ursabot.git\",\n+ \"clone_url\": \"https://github.com/ursa-labs/ursabot.git\",\n+ \"svn_url\": \"https://github.com/ursa-labs/ursabot\",\n+ \"homepage\": null,\n+ \"size\": 898,\n+ \"stargazers_count\": 1,\n+ \"watchers_count\": 1,\n+ \"language\": \"Jupyter Notebook\",\n+ \"has_issues\": true,\n+ \"has_projects\": true,\n+ \"has_downloads\": true,\n+ \"has_wiki\": true,\n+ \"has_pages\": false,\n+ \"forks_count\": 0,\n+ \"mirror_url\": null,\n+ \"archived\": false,\n+ \"disabled\": false,\n+ \"open_issues_count\": 19,\n+ \"license\": null,\n+ \"forks\": 0,\n+ \"open_issues\": 19,\n+ \"watchers\": 1,\n+ \"default_branch\": \"master\"\n+ }\n+ },\n+ \"base\": {\n+ \"label\": \"ursa-labs:master\",\n+ \"ref\": \"master\",\n+ \"sha\": \"a162ad254b589b924db47e057791191b39613fd5\",\n+ \"user\": {\n+ \"login\": \"ursa-labs\",\n+ \"id\": 46514972,\n+ \"node_id\": \"MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy\",\n+ \"avatar_url\": \"https://avatars2.githubusercontent.com/u/46514972?v=4\",\n+ \"gravatar_id\": \"\",\n+ \"url\": \"https://api.github.com/users/ursa-labs\",\n+ \"html_url\": \"https://github.com/ursa-labs\",\n+ \"followers_url\": \"https://api.github.com/users/ursa-labs/followers\",\n+ \"following_url\": \"https://api.github.com/users/ursa-labs/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/ursa-labs/gists{/gist_id}\",\n+ \"starred_url\": \"https://api.github.com/users/ursa-labs/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/ursa-labs/subscriptions\",\n+ \"organizations_url\": \"https://api.github.com/users/ursa-labs/orgs\",\n+ \"repos_url\": \"https://api.github.com/users/ursa-labs/repos\",\n+ \"events_url\": \"https://api.github.com/users/ursa-labs/events{/privacy}\",\n+ \"received_events_url\": \"https://api.github.com/users/ursa-labs/received_events\",\n+ \"type\": \"Organization\",\n+ \"site_admin\": false\n+ },\n+ \"repo\": {\n+ \"id\": 169101701,\n+ \"node_id\": \"MDEwOlJlcG9zaXRvcnkxNjkxMDE3MDE=\",\n+ \"name\": \"ursabot\",\n+ \"full_name\": \"ursa-labs/ursabot\",\n+ \"private\": false,\n+ \"owner\": {\n+ \"login\": \"ursa-labs\",\n+ \"id\": 46514972,\n+ \"node_id\": \"MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy\",\n+ \"avatar_url\": \"https://avatars2.githubusercontent.com/u/46514972?v=4\",\n+ \"gravatar_id\": \"\",\n+ \"url\": \"https://api.github.com/users/ursa-labs\",\n+ \"html_url\": \"https://github.com/ursa-labs\",\n+ \"followers_url\": \"https://api.github.com/users/ursa-labs/followers\",\n+ \"following_url\": \"https://api.github.com/users/ursa-labs/following{/other_user}\",\n+ \"gists_url\": \"https://api.github.com/users/ursa-labs/gists{/gist_id}\",\n+ \"starred_url\": \"https://api.github.com/users/ursa-labs/starred{/owner}{/repo}\",\n+ \"subscriptions_url\": \"https://api.github.com/users/ursa-labs/subscriptions\",\n+ \"organizations_url\": \"https://api.github.com/users/ursa-labs/orgs\",\n+ \"repos_url\": \"https://api.github.com/users/ursa-labs/repos\",\n+ \"events_url\": \"https://api.github.com/users/ursa-labs/events{/privacy}\",\n+ \"received_events_url\": \"https://api.github.com/users/ursa-labs/received_events\",\n+ \"type\": \"Organization\",\n+ \"site_admin\": false\n+ },\n+ \"html_url\": \"https://github.com/ursa-labs/ursabot\",\n+ \"description\": null,\n+ \"fork\": false,\n+ \"url\": \"https://api.github.com/repos/ursa-labs/ursabot\",\n+ \"forks_url\": \"https://api.github.com/repos/ursa-labs/ursabot/forks\",\n+ \"keys_url\": \"https://api.github.com/repos/ursa-labs/ursabot/keys{/key_id}\",\n+ \"collaborators_url\": \"https://api.github.com/repos/ursa-labs/ursabot/collaborators{/collaborator}\",\n+ \"teams_url\": \"https://api.github.com/repos/ursa-labs/ursabot/teams\",\n+ \"hooks_url\": \"https://api.github.com/repos/ursa-labs/ursabot/hooks\",\n+ \"issue_events_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/events{/number}\",\n+ \"events_url\": \"https://api.github.com/repos/ursa-labs/ursabot/events\",\n+ \"assignees_url\": \"https://api.github.com/repos/ursa-labs/ursabot/assignees{/user}\",\n+ \"branches_url\": \"https://api.github.com/repos/ursa-labs/ursabot/branches{/branch}\",\n+ \"tags_url\": \"https://api.github.com/repos/ursa-labs/ursabot/tags\",\n+ \"blobs_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/blobs{/sha}\",\n+ \"git_tags_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/tags{/sha}\",\n+ \"git_refs_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/refs{/sha}\",\n+ \"trees_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/trees{/sha}\",\n+ \"statuses_url\": \"https://api.github.com/repos/ursa-labs/ursabot/statuses/{sha}\",\n+ \"languages_url\": \"https://api.github.com/repos/ursa-labs/ursabot/languages\",\n+ \"stargazers_url\": \"https://api.github.com/repos/ursa-labs/ursabot/stargazers\",\n+ \"contributors_url\": \"https://api.github.com/repos/ursa-labs/ursabot/contributors\",\n+ \"subscribers_url\": \"https://api.github.com/repos/ursa-labs/ursabot/subscribers\",\n+ \"subscription_url\": \"https://api.github.com/repos/ursa-labs/ursabot/subscription\",\n+ \"commits_url\": \"https://api.github.com/repos/ursa-labs/ursabot/commits{/sha}\",\n+ \"git_commits_url\": \"https://api.github.com/repos/ursa-labs/ursabot/git/commits{/sha}\",\n+ \"comments_url\": \"https://api.github.com/repos/ursa-labs/ursabot/comments{/number}\",\n+ \"issue_comment_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/comments{/number}\",\n+ \"contents_url\": \"https://api.github.com/repos/ursa-labs/ursabot/contents/{+path}\",\n+ \"compare_url\": \"https://api.github.com/repos/ursa-labs/ursabot/compare/{base}...{head}\",\n+ \"merges_url\": \"https://api.github.com/repos/ursa-labs/ursabot/merges\",\n+ \"archive_url\": \"https://api.github.com/repos/ursa-labs/ursabot/{archive_format}{/ref}\",\n+ \"downloads_url\": \"https://api.github.com/repos/ursa-labs/ursabot/downloads\",\n+ \"issues_url\": \"https://api.github.com/repos/ursa-labs/ursabot/issues{/number}\",\n+ \"pulls_url\": \"https://api.github.com/repos/ursa-labs/ursabot/pulls{/number}\",\n+ \"milestones_url\": \"https://api.github.com/repos/ursa-labs/ursabot/milestones{/number}\",\n+ \"notifications_url\": \"https://api.github.com/repos/ursa-labs/ursabot/notifications{?since,all,participating}\",\n+ \"labels_url\": \"https://api.github.com/repos/ursa-labs/ursabot/labels{/name}\",\n+ \"releases_url\": \"https://api.github.com/repos/ursa-labs/ursabot/releases{/id}\",\n+ \"deployments_url\": \"https://api.github.com/repos/ursa-labs/ursabot/deployments\",\n+ \"created_at\": \"2019-02-04T15:40:31Z\",\n+ \"updated_at\": \"2019-04-04T17:49:10Z\",\n+ \"pushed_at\": \"2019-04-05T12:01:40Z\",\n+ \"git_url\": \"git://github.com/ursa-labs/ursabot.git\",\n+ \"ssh_url\": \"git@github.com:ursa-labs/ursabot.git\",\n+ \"clone_url\": \"https://github.com/ursa-labs/ursabot.git\",\n+ \"svn_url\": \"https://github.com/ursa-labs/ursabot\",\n+ \"homepage\": null,\n+ \"size\": 898,\n+ \"stargazers_count\": 1,\n+ \"watchers_count\": 1,\n+ \"language\": \"Jupyter Notebook\",\n+ \"has_issues\": true,\n+ \"has_projects\": true,\n+ \"has_downloads\": true,\n+ \"has_wiki\": true,\n+ \"has_pages\": false,\n+ \"forks_count\": 0,\n+ \"mirror_url\": null,\n+ \"archived\": false,\n+ \"disabled\": false,\n+ \"open_issues_count\": 19,\n+ \"license\": null,\n+ \"forks\": 0,\n+ \"open_issues\": 19,\n+ \"watchers\": 1,\n+ \"default_branch\": \"master\"\n+ }\n+ },\n+ \"_links\": {\n+ \"self\": {\n+ \"href\": \"https://api.github.com/repos/ursa-labs/ursabot/pulls/26\"\n+ },\n+ \"html\": {\n+ \"href\": \"https://github.com/ursa-labs/ursabot/pull/26\"\n+ },\n+ \"issue\": {\n+ \"href\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26\"\n+ },\n+ \"comments\": {\n+ \"href\": \"https://api.github.com/repos/ursa-labs/ursabot/issues/26/comments\"\n+ },\n+ \"review_comments\": {\n+ \"href\": \"https://api.github.com/repos/ursa-labs/ursabot/pulls/26/comments\"\n+ },\n+ \"review_comment\": {\n+ \"href\": \"https://api.github.com/repos/ursa-labs/ursabot/pulls/comments{/number}\"\n+ },\n+ \"commits\": {\n+ \"href\": \"https://api.github.com/repos/ursa-labs/ursabot/pulls/26/commits\"\n+ },\n+ \"statuses\": {\n+ \"href\": \"https://api.github.com/repos/ursa-labs/ursabot/statuses/2705da2b616b98fa6010a25813c5a7a27456f71d\"\n+ }\n+ },\n+ \"author_association\": \"MEMBER\",\n+ \"merged\": false,\n+ \"mergeable\": true,\n+ \"rebaseable\": true,\n+ \"mergeable_state\": \"unstable\",\n+ \"merged_by\": null,\n+ \"comments\": 5,\n+ \"review_comments\": 0,\n+ \"maintainer_can_modify\": false,\n+ \"commits\": 2,\n+ \"additions\": 1124,\n+ \"deletions\": 0,\n+ \"changed_files\": 7\n+}"
- },
- {
- "sha": "e87b27d2d7b4956d15f7468488b96cf6a06686f4",
- "filename": "ursabot/tests/test_hooks.py",
- "status": "added",
- "additions": 116,
- "deletions": 0,
- "changes": 116,
- "blob_url": "https://github.com/ursa-labs/ursabot/blob/70267dee34884e4b972388e1b30d57f6248c58d0/ursabot/tests/test_hooks.py",
- "raw_url": "https://github.com/ursa-labs/ursabot/raw/70267dee34884e4b972388e1b30d57f6248c58d0/ursabot/tests/test_hooks.py",
- "contents_url": "https://api.github.com/repos/ursa-labs/ursabot/contents/ursabot/tests/test_hooks.py?ref=70267dee34884e4b972388e1b30d57f6248c58d0",
- "patch": "@@ -0,0 +1,116 @@\n+import json\n+from pathlib import Path\n+from twisted.trial import unittest\n+\n+from buildbot.test.util.misc import TestReactorMixin\n+from buildbot.test.fake.httpclientservice import \\\n+ HTTPClientService as FakeHTTPClientService\n+from buildbot.test.unit.test_www_hooks_github import (\n+ _prepare_request, _prepare_github_change_hook)\n+\n+from ursabot.utils import ensure_deferred\n+from ursabot.hooks import GithubHook\n+\n+\n+class ChangeHookTestCase(unittest.TestCase, TestReactorMixin):\n+\n+ klass = None\n+\n+ @ensure_deferred\n+ async def setUp(self):\n+ self.setUpTestReactor()\n+\n+ assert self.klass is not None\n+ self.hook = _prepare_github_change_hook(self, **{'class': self.klass})\n+ self.master = self.hook.master\n+ self.http = await FakeHTTPClientService.getFakeService(\n+ self.master, self, 'https://api.github.com',\n+ headers={'User-Agent': 'Buildbot'}, debug=False, verify=False)\n+\n+ await self.master.startService()\n+\n+ @ensure_deferred\n+ async def tearDown(self):\n+ await self.master.stopService()\n+\n+ async def trigger(self, event, payload, headers=None, _secret=None):\n+ payload = json.dumps(payload).encode()\n+ request = _prepare_request(event, payload, _secret=_secret,\n+ headers=headers)\n+ await request.test_render(self.hook)\n+ return request\n+\n+ def load_fixture(self, name):\n+ path = Path(__file__).parent / 'fixtures' / f'{name}.json'\n+ with path.open('r') as fp:\n+ return json.load(fp)\n+\n+\n+class TestGithubHook(ChangeHookTestCase):\n+\n+ klass = GithubHook\n+\n+ @ensure_deferred\n+ async def test_ping(self):\n+ await self.trigger('ping', {})\n+ assert len(self.hook.master.data.updates.changesAdded) == 0\n+\n+ @ensure_deferred\n+ async def test_issue_comment_not_mentioning_ursabot(self):\n+ payload = self.load_fixture('issue-comment-not-mentioning-ursabot')\n+ await self.trigger('issue_comment', payload=payload)\n+ assert len(self.hook.master.data.updates.changesAdded) == 0\n+\n+ @ensure_deferred\n+ async def test_issue_comment_by_ursabot(self):\n+ payload = self.load_fixture('issue-comment-by-ursabot')\n+ await self.trigger('issue_comment', payload=payload)\n+ assert len(self.hook.master.data.updates.changesAdded) == 0\n+\n+ @ensure_deferred\n+ async def test_issue_comment_with_empty_command(self):\n+ # responds to the comment\n+ request_json = {'body': 'Unknown command \"\"'}\n+ response_json = ''\n+ self.http.expect('post', '/repos/ursa-labs/ursabot/issues/26/comments',\n+ json=request_json, content_json=response_json)\n+\n+ payload = self.load_fixture('issue-comment-with-empty-command')\n+ await self.trigger('issue_comment', payload=payload)\n+ assert len(self.hook.master.data.updates.changesAdded) == 0\n+\n+ @ensure_deferred\n+ async def test_issue_comment_without_pull_request(self):\n+ # responds to the comment\n+ request_json = {\n+ 'body': 'Ursabot only listens to pull request comments!'\n+ }\n+ response_json = ''\n+ self.http.expect('post', '/repos/ursa-labs/ursabot/issues/19/comments',\n+ json=request_json, content_json=response_json)\n+\n+ payload = self.load_fixture('issue-comment-without-pull-request')\n+ await self.trigger('issue_comment', payload=payload)\n+ assert len(self.hook.master.data.updates.changesAdded) == 0\n+\n+ @ensure_deferred\n+ async def test_issue_comment_build_command(self):\n+ # handle_issue_comment queries the pull request\n+ request_json = self.load_fixture('pull-request-26')\n+ self.http.expect('get', '/repos/ursa-labs/ursabot/pulls/26',\n+ content_json=request_json)\n+ # tigger handle_pull_request which fetches the commit\n+ request_json = self.load_fixture('pull-request-26-commit')\n+ commit = '2705da2b616b98fa6010a25813c5a7a27456f71d'\n+ self.http.expect('get', f'/repos/ursa-labs/ursabot/commits/{commit}',\n+ content_json=request_json)\n+\n+ # then responds to the comment\n+ request_json = {'body': \"I've successfully started builds for this PR\"}\n+ response_json = ''\n+ self.http.expect('post', '/repos/ursa-labs/ursabot/issues/26/comments',\n+ json=request_json, content_json=response_json)\n+\n+ payload = self.load_fixture('issue-comment-build-command')\n+ await self.trigger('issue_comment', payload=payload)\n+ assert len(self.hook.master.data.updates.changesAdded) == 1"
- },
- {
- "sha": "3ff0e88660cf186420e8bc672735e4d446963192",
- "filename": "ursabot/utils.py",
- "status": "added",
- "additions": 10,
- "deletions": 0,
- "changes": 10,
- "blob_url": "https://github.com/ursa-labs/ursabot/blob/70267dee34884e4b972388e1b30d57f6248c58d0/ursabot/utils.py",
- "raw_url": "https://github.com/ursa-labs/ursabot/raw/70267dee34884e4b972388e1b30d57f6248c58d0/ursabot/utils.py",
- "contents_url": "https://api.github.com/repos/ursa-labs/ursabot/contents/ursabot/utils.py?ref=70267dee34884e4b972388e1b30d57f6248c58d0",
- "patch": "@@ -0,0 +1,10 @@\n+import functools\n+from twisted.internet import defer\n+\n+\n+def ensure_deferred(f):\n+ @functools.wraps(f)\n+ def wrapper(*args, **kwargs):\n+ result = f(*args, **kwargs)\n+ return defer.ensureDeferred(result)\n+ return wrapper"
- }
-]
\ No newline at end of file
diff --git a/dev/archery/archery/tests/fixtures/pull-request-26.json b/dev/archery/archery/tests/fixtures/pull-request-26.json
deleted file mode 100644
index d295afb..0000000
--- a/dev/archery/archery/tests/fixtures/pull-request-26.json
+++ /dev/null
@@ -1,329 +0,0 @@
-{
- "url": "https://api.github.com/repos/ursa-labs/ursabot/pulls/26",
- "id": 267785552,
- "node_id": "MDExOlB1bGxSZXF1ZXN0MjY3Nzg1NTUy",
- "html_url": "https://github.com/ursa-labs/ursabot/pull/26",
- "diff_url": "https://github.com/ursa-labs/ursabot/pull/26.diff",
- "patch_url": "https://github.com/ursa-labs/ursabot/pull/26.patch",
- "issue_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/26",
- "number": 26,
- "state": "open",
- "locked": false,
- "title": "Unittests for GithubHook",
- "user": {
- "login": "kszucs",
- "id": 961747,
- "node_id": "MDQ6VXNlcjk2MTc0Nw==",
- "avatar_url": "https://avatars1.githubusercontent.com/u/961747?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/kszucs",
- "html_url": "https://github.com/kszucs",
- "followers_url": "https://api.github.com/users/kszucs/followers",
- "following_url": "https://api.github.com/users/kszucs/following{/other_user}",
- "gists_url": "https://api.github.com/users/kszucs/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/kszucs/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/kszucs/subscriptions",
- "organizations_url": "https://api.github.com/users/kszucs/orgs",
- "repos_url": "https://api.github.com/users/kszucs/repos",
- "events_url": "https://api.github.com/users/kszucs/events{/privacy}",
- "received_events_url": "https://api.github.com/users/kszucs/received_events",
- "type": "User",
- "site_admin": false
- },
- "body": "",
- "body_html": "",
- "body_text": "",
- "created_at": "2019-04-05T11:22:15Z",
- "updated_at": "2019-04-05T12:01:40Z",
- "closed_at": null,
- "merged_at": null,
- "merge_commit_sha": "cc5dc3606988b3824be54df779ed2028776113cb",
- "assignee": null,
- "assignees": [],
- "requested_reviewers": [],
- "requested_teams": [],
- "labels": [],
- "milestone": null,
- "commits_url": "https://api.github.com/repos/ursa-labs/ursabot/pulls/26/commits",
- "review_comments_url": "https://api.github.com/repos/ursa-labs/ursabot/pulls/26/comments",
- "review_comment_url": "https://api.github.com/repos/ursa-labs/ursabot/pulls/comments{/number}",
- "comments_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/26/comments",
- "statuses_url": "https://api.github.com/repos/ursa-labs/ursabot/statuses/2705da2b616b98fa6010a25813c5a7a27456f71d",
- "head": {
- "label": "ursa-labs:test-hook",
- "ref": "test-hook",
- "sha": "2705da2b616b98fa6010a25813c5a7a27456f71d",
- "user": {
- "login": "ursa-labs",
- "id": 46514972,
- "node_id": "MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy",
- "avatar_url": "https://avatars2.githubusercontent.com/u/46514972?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/ursa-labs",
- "html_url": "https://github.com/ursa-labs",
- "followers_url": "https://api.github.com/users/ursa-labs/followers",
- "following_url": "https://api.github.com/users/ursa-labs/following{/other_user}",
- "gists_url": "https://api.github.com/users/ursa-labs/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/ursa-labs/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/ursa-labs/subscriptions",
- "organizations_url": "https://api.github.com/users/ursa-labs/orgs",
- "repos_url": "https://api.github.com/users/ursa-labs/repos",
- "events_url": "https://api.github.com/users/ursa-labs/events{/privacy}",
- "received_events_url": "https://api.github.com/users/ursa-labs/received_events",
- "type": "Organization",
- "site_admin": false
- },
- "repo": {
- "id": 169101701,
- "node_id": "MDEwOlJlcG9zaXRvcnkxNjkxMDE3MDE=",
- "name": "ursabot",
- "full_name": "ursa-labs/ursabot",
- "private": false,
- "owner": {
- "login": "ursa-labs",
- "id": 46514972,
- "node_id": "MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy",
- "avatar_url": "https://avatars2.githubusercontent.com/u/46514972?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/ursa-labs",
- "html_url": "https://github.com/ursa-labs",
- "followers_url": "https://api.github.com/users/ursa-labs/followers",
- "following_url": "https://api.github.com/users/ursa-labs/following{/other_user}",
- "gists_url": "https://api.github.com/users/ursa-labs/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/ursa-labs/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/ursa-labs/subscriptions",
- "organizations_url": "https://api.github.com/users/ursa-labs/orgs",
- "repos_url": "https://api.github.com/users/ursa-labs/repos",
- "events_url": "https://api.github.com/users/ursa-labs/events{/privacy}",
- "received_events_url": "https://api.github.com/users/ursa-labs/received_events",
- "type": "Organization",
- "site_admin": false
- },
- "html_url": "https://github.com/ursa-labs/ursabot",
- "description": null,
- "fork": false,
- "url": "https://api.github.com/repos/ursa-labs/ursabot",
- "forks_url": "https://api.github.com/repos/ursa-labs/ursabot/forks",
- "keys_url": "https://api.github.com/repos/ursa-labs/ursabot/keys{/key_id}",
- "collaborators_url": "https://api.github.com/repos/ursa-labs/ursabot/collaborators{/collaborator}",
- "teams_url": "https://api.github.com/repos/ursa-labs/ursabot/teams",
- "hooks_url": "https://api.github.com/repos/ursa-labs/ursabot/hooks",
- "issue_events_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/events{/number}",
- "events_url": "https://api.github.com/repos/ursa-labs/ursabot/events",
- "assignees_url": "https://api.github.com/repos/ursa-labs/ursabot/assignees{/user}",
- "branches_url": "https://api.github.com/repos/ursa-labs/ursabot/branches{/branch}",
- "tags_url": "https://api.github.com/repos/ursa-labs/ursabot/tags",
- "blobs_url": "https://api.github.com/repos/ursa-labs/ursabot/git/blobs{/sha}",
- "git_tags_url": "https://api.github.com/repos/ursa-labs/ursabot/git/tags{/sha}",
- "git_refs_url": "https://api.github.com/repos/ursa-labs/ursabot/git/refs{/sha}",
- "trees_url": "https://api.github.com/repos/ursa-labs/ursabot/git/trees{/sha}",
- "statuses_url": "https://api.github.com/repos/ursa-labs/ursabot/statuses/{sha}",
- "languages_url": "https://api.github.com/repos/ursa-labs/ursabot/languages",
- "stargazers_url": "https://api.github.com/repos/ursa-labs/ursabot/stargazers",
- "contributors_url": "https://api.github.com/repos/ursa-labs/ursabot/contributors",
- "subscribers_url": "https://api.github.com/repos/ursa-labs/ursabot/subscribers",
- "subscription_url": "https://api.github.com/repos/ursa-labs/ursabot/subscription",
- "commits_url": "https://api.github.com/repos/ursa-labs/ursabot/commits{/sha}",
- "git_commits_url": "https://api.github.com/repos/ursa-labs/ursabot/git/commits{/sha}",
- "comments_url": "https://api.github.com/repos/ursa-labs/ursabot/comments{/number}",
- "issue_comment_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/comments{/number}",
- "contents_url": "https://api.github.com/repos/ursa-labs/ursabot/contents/{+path}",
- "compare_url": "https://api.github.com/repos/ursa-labs/ursabot/compare/{base}...{head}",
- "merges_url": "https://api.github.com/repos/ursa-labs/ursabot/merges",
- "archive_url": "https://api.github.com/repos/ursa-labs/ursabot/{archive_format}{/ref}",
- "downloads_url": "https://api.github.com/repos/ursa-labs/ursabot/downloads",
- "issues_url": "https://api.github.com/repos/ursa-labs/ursabot/issues{/number}",
- "pulls_url": "https://api.github.com/repos/ursa-labs/ursabot/pulls{/number}",
- "milestones_url": "https://api.github.com/repos/ursa-labs/ursabot/milestones{/number}",
- "notifications_url": "https://api.github.com/repos/ursa-labs/ursabot/notifications{?since,all,participating}",
- "labels_url": "https://api.github.com/repos/ursa-labs/ursabot/labels{/name}",
- "releases_url": "https://api.github.com/repos/ursa-labs/ursabot/releases{/id}",
- "deployments_url": "https://api.github.com/repos/ursa-labs/ursabot/deployments",
- "created_at": "2019-02-04T15:40:31Z",
- "updated_at": "2019-04-04T17:49:10Z",
- "pushed_at": "2019-04-05T12:01:40Z",
- "git_url": "git://github.com/ursa-labs/ursabot.git",
- "ssh_url": "git@github.com:ursa-labs/ursabot.git",
- "clone_url": "https://github.com/ursa-labs/ursabot.git",
- "svn_url": "https://github.com/ursa-labs/ursabot",
- "homepage": null,
- "size": 898,
- "stargazers_count": 1,
- "watchers_count": 1,
- "language": "Jupyter Notebook",
- "has_issues": true,
- "has_projects": true,
- "has_downloads": true,
- "has_wiki": true,
- "has_pages": false,
- "forks_count": 0,
- "mirror_url": null,
- "archived": false,
- "disabled": false,
- "open_issues_count": 19,
- "license": null,
- "forks": 0,
- "open_issues": 19,
- "watchers": 1,
- "default_branch": "master"
- }
- },
- "base": {
- "label": "ursa-labs:master",
- "ref": "master",
- "sha": "a162ad254b589b924db47e057791191b39613fd5",
- "user": {
- "login": "ursa-labs",
- "id": 46514972,
- "node_id": "MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy",
- "avatar_url": "https://avatars2.githubusercontent.com/u/46514972?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/ursa-labs",
- "html_url": "https://github.com/ursa-labs",
- "followers_url": "https://api.github.com/users/ursa-labs/followers",
- "following_url": "https://api.github.com/users/ursa-labs/following{/other_user}",
- "gists_url": "https://api.github.com/users/ursa-labs/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/ursa-labs/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/ursa-labs/subscriptions",
- "organizations_url": "https://api.github.com/users/ursa-labs/orgs",
- "repos_url": "https://api.github.com/users/ursa-labs/repos",
- "events_url": "https://api.github.com/users/ursa-labs/events{/privacy}",
- "received_events_url": "https://api.github.com/users/ursa-labs/received_events",
- "type": "Organization",
- "site_admin": false
- },
- "repo": {
- "id": 169101701,
- "node_id": "MDEwOlJlcG9zaXRvcnkxNjkxMDE3MDE=",
- "name": "ursabot",
- "full_name": "ursa-labs/ursabot",
- "private": false,
- "owner": {
- "login": "ursa-labs",
- "id": 46514972,
- "node_id": "MDEyOk9yZ2FuaXphdGlvbjQ2NTE0OTcy",
- "avatar_url": "https://avatars2.githubusercontent.com/u/46514972?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/ursa-labs",
- "html_url": "https://github.com/ursa-labs",
- "followers_url": "https://api.github.com/users/ursa-labs/followers",
- "following_url": "https://api.github.com/users/ursa-labs/following{/other_user}",
- "gists_url": "https://api.github.com/users/ursa-labs/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/ursa-labs/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/ursa-labs/subscriptions",
- "organizations_url": "https://api.github.com/users/ursa-labs/orgs",
- "repos_url": "https://api.github.com/users/ursa-labs/repos",
- "events_url": "https://api.github.com/users/ursa-labs/events{/privacy}",
- "received_events_url": "https://api.github.com/users/ursa-labs/received_events",
- "type": "Organization",
- "site_admin": false
- },
- "html_url": "https://github.com/ursa-labs/ursabot",
- "description": null,
- "fork": false,
- "url": "https://api.github.com/repos/ursa-labs/ursabot",
- "forks_url": "https://api.github.com/repos/ursa-labs/ursabot/forks",
- "keys_url": "https://api.github.com/repos/ursa-labs/ursabot/keys{/key_id}",
- "collaborators_url": "https://api.github.com/repos/ursa-labs/ursabot/collaborators{/collaborator}",
- "teams_url": "https://api.github.com/repos/ursa-labs/ursabot/teams",
- "hooks_url": "https://api.github.com/repos/ursa-labs/ursabot/hooks",
- "issue_events_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/events{/number}",
- "events_url": "https://api.github.com/repos/ursa-labs/ursabot/events",
- "assignees_url": "https://api.github.com/repos/ursa-labs/ursabot/assignees{/user}",
- "branches_url": "https://api.github.com/repos/ursa-labs/ursabot/branches{/branch}",
- "tags_url": "https://api.github.com/repos/ursa-labs/ursabot/tags",
- "blobs_url": "https://api.github.com/repos/ursa-labs/ursabot/git/blobs{/sha}",
- "git_tags_url": "https://api.github.com/repos/ursa-labs/ursabot/git/tags{/sha}",
- "git_refs_url": "https://api.github.com/repos/ursa-labs/ursabot/git/refs{/sha}",
- "trees_url": "https://api.github.com/repos/ursa-labs/ursabot/git/trees{/sha}",
- "statuses_url": "https://api.github.com/repos/ursa-labs/ursabot/statuses/{sha}",
- "languages_url": "https://api.github.com/repos/ursa-labs/ursabot/languages",
- "stargazers_url": "https://api.github.com/repos/ursa-labs/ursabot/stargazers",
- "contributors_url": "https://api.github.com/repos/ursa-labs/ursabot/contributors",
- "subscribers_url": "https://api.github.com/repos/ursa-labs/ursabot/subscribers",
- "subscription_url": "https://api.github.com/repos/ursa-labs/ursabot/subscription",
- "commits_url": "https://api.github.com/repos/ursa-labs/ursabot/commits{/sha}",
- "git_commits_url": "https://api.github.com/repos/ursa-labs/ursabot/git/commits{/sha}",
- "comments_url": "https://api.github.com/repos/ursa-labs/ursabot/comments{/number}",
- "issue_comment_url": "https://api.github.com/repos/ursa-labs/ursabot/issues/comments{/number}",
- "contents_url": "https://api.github.com/repos/ursa-labs/ursabot/contents/{+path}",
- "compare_url": "https://api.github.com/repos/ursa-labs/ursabot/compare/{base}...{head}",
- "merges_url": "https://api.github.com/repos/ursa-labs/ursabot/merges",
- "archive_url": "https://api.github.com/repos/ursa-labs/ursabot/{archive_format}{/ref}",
- "downloads_url": "https://api.github.com/repos/ursa-labs/ursabot/downloads",
- "issues_url": "https://api.github.com/repos/ursa-labs/ursabot/issues{/number}",
- "pulls_url": "https://api.github.com/repos/ursa-labs/ursabot/pulls{/number}",
- "milestones_url": "https://api.github.com/repos/ursa-labs/ursabot/milestones{/number}",
- "notifications_url": "https://api.github.com/repos/ursa-labs/ursabot/notifications{?since,all,participating}",
- "labels_url": "https://api.github.com/repos/ursa-labs/ursabot/labels{/name}",
- "releases_url": "https://api.github.com/repos/ursa-labs/ursabot/releases{/id}",
- "deployments_url": "https://api.github.com/repos/ursa-labs/ursabot/deployments",
- "created_at": "2019-02-04T15:40:31Z",
- "updated_at": "2019-04-04T17:49:10Z",
- "pushed_at": "2019-04-05T12:01:40Z",
- "git_url": "git://github.com/ursa-labs/ursabot.git",
- "ssh_url": "git@github.com:ursa-labs/ursabot.git",
- "clone_url": "https://github.com/ursa-labs/ursabot.git",
- "svn_url": "https://github.com/ursa-labs/ursabot",
- "homepage": null,
- "size": 898,
- "stargazers_count": 1,
- "watchers_count": 1,
- "language": "Jupyter Notebook",
- "has_issues": true,
- "has_projects": true,
- "has_downloads": true,
- "has_wiki": true,
- "has_pages": false,
- "forks_count": 0,
- "mirror_url": null,
- "archived": false,
- "disabled": false,
- "open_issues_count": 19,
- "license": null,
- "forks": 0,
- "open_issues": 19,
- "watchers": 1,
- "default_branch": "master"
- }
- },
- "_links": {
- "self": {
- "href": "https://api.github.com/repos/ursa-labs/ursabot/pulls/26"
- },
- "html": {
- "href": "https://github.com/ursa-labs/ursabot/pull/26"
- },
- "issue": {
- "href": "https://api.github.com/repos/ursa-labs/ursabot/issues/26"
- },
- "comments": {
- "href": "https://api.github.com/repos/ursa-labs/ursabot/issues/26/comments"
- },
- "review_comments": {
- "href": "https://api.github.com/repos/ursa-labs/ursabot/pulls/26/comments"
- },
- "review_comment": {
- "href": "https://api.github.com/repos/ursa-labs/ursabot/pulls/comments{/number}"
- },
- "commits": {
- "href": "https://api.github.com/repos/ursa-labs/ursabot/pulls/26/commits"
- },
- "statuses": {
- "href": "https://api.github.com/repos/ursa-labs/ursabot/statuses/2705da2b616b98fa6010a25813c5a7a27456f71d"
- }
- },
- "author_association": "MEMBER",
- "merged": false,
- "mergeable": true,
- "rebaseable": true,
- "mergeable_state": "unstable",
- "merged_by": null,
- "comments": 5,
- "review_comments": 0,
- "maintainer_can_modify": false,
- "commits": 2,
- "additions": 1124,
- "deletions": 0,
- "changed_files": 7
-}
\ No newline at end of file
diff --git a/dev/archery/archery/tests/test_benchmarks.py b/dev/archery/archery/tests/test_benchmarks.py
deleted file mode 100644
index fab1e8d..0000000
--- a/dev/archery/archery/tests/test_benchmarks.py
+++ /dev/null
@@ -1,383 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-import json
-
-from archery.benchmark.codec import JsonEncoder
-from archery.benchmark.core import Benchmark, median
-from archery.benchmark.compare import (
- BenchmarkComparator, RunnerComparator
-)
-from archery.benchmark.google import (
- GoogleBenchmark, GoogleBenchmarkObservation
-)
-from archery.benchmark.runner import StaticBenchmarkRunner
-
-
-def test_benchmark_comparator():
- unit = "micros"
-
- assert not BenchmarkComparator(
- Benchmark("contender", unit, True, [10], unit, [1]),
- Benchmark("baseline", unit, True, [20], unit, [1]),
- ).regression
-
- assert BenchmarkComparator(
- Benchmark("contender", unit, False, [10], unit, [1]),
- Benchmark("baseline", unit, False, [20], unit, [1]),
- ).regression
-
- assert BenchmarkComparator(
- Benchmark("contender", unit, True, [20], unit, [1]),
- Benchmark("baseline", unit, True, [10], unit, [1]),
- ).regression
-
- assert not BenchmarkComparator(
- Benchmark("contender", unit, False, [20], unit, [1]),
- Benchmark("baseline", unit, False, [10], unit, [1]),
- ).regression
-
-
-def test_static_runner_from_json_not_a_regression():
- archery_result = {
- "suites": [
- {
- "name": "arrow-value-parsing-benchmark",
- "benchmarks": [
- {
- "name": "FloatParsing<DoubleType>",
- "unit": "items_per_second",
- "less_is_better": False,
- "values": [
- 109941112.87296811
- ],
- "time_unit": "ns",
- "times": [
- 9095.800104330105
- ]
- },
- ]
- }
- ]
- }
-
- contender = StaticBenchmarkRunner.from_json(json.dumps(archery_result))
- baseline = StaticBenchmarkRunner.from_json(json.dumps(archery_result))
- [comparison] = RunnerComparator(contender, baseline).comparisons
- assert not comparison.regression
-
-
-def test_static_runner_from_json_regression():
- archery_result = {
- "suites": [
- {
- "name": "arrow-value-parsing-benchmark",
- "benchmarks": [
- {
- "name": "FloatParsing<DoubleType>",
- "unit": "items_per_second",
- "less_is_better": False,
- "values": [
- 109941112.87296811
- ],
- "time_unit": "ns",
- "times": [
- 9095.800104330105
- ]
- },
- ]
- }
- ]
- }
-
- contender = StaticBenchmarkRunner.from_json(json.dumps(archery_result))
-
- # introduce artificial regression
- archery_result['suites'][0]['benchmarks'][0]['values'][0] *= 2
- baseline = StaticBenchmarkRunner.from_json(json.dumps(archery_result))
-
- [comparison] = RunnerComparator(contender, baseline).comparisons
- assert comparison.regression
-
-
-def test_benchmark_median():
- assert median([10]) == 10
- assert median([1, 2, 3]) == 2
- assert median([1, 2]) == 1.5
- assert median([1, 2, 3, 4]) == 2.5
- assert median([1, 1, 1, 1]) == 1
- try:
- median([])
- assert False
- except ValueError:
- pass
-
-
-def assert_benchmark(name, google_result, archery_result):
- observation = GoogleBenchmarkObservation(**google_result)
- benchmark = GoogleBenchmark(name, [observation])
- result = json.dumps(benchmark, cls=JsonEncoder)
- assert json.loads(result) == archery_result
-
-
-def test_items_per_second():
- name = "ArrayArrayKernel<AddChecked, UInt8Type>/32768/0"
- google_result = {
- "cpu_time": 116292.58886653671,
- "items_per_second": 281772039.9844759,
- "iterations": 5964,
- "name": name,
- "null_percent": 0.0,
- "real_time": 119811.77313729875,
- "repetition_index": 0,
- "repetitions": 0,
- "run_name": name,
- "run_type": "iteration",
- "size": 32768.0,
- "threads": 1,
- "time_unit": "ns",
- }
- archery_result = {
- "counters": {"iterations": 5964,
- "null_percent": 0.0,
- "repetition_index": 0,
- "repetitions": 0,
- "run_name": name,
- "threads": 1},
- "name": name,
- "unit": "items_per_second",
- "less_is_better": False,
- "values": [281772039.9844759],
- "time_unit": "ns",
- "times": [119811.77313729875],
- }
- assert "items_per_second" in google_result
- assert "bytes_per_second" not in google_result
- assert_benchmark(name, google_result, archery_result)
-
-
-def test_bytes_per_second():
- name = "BufferOutputStreamLargeWrites/real_time"
- google_result = {
- "bytes_per_second": 1890209037.3405428,
- "cpu_time": 17018127.659574457,
- "iterations": 47,
- "name": name,
- "real_time": 17458386.53190963,
- "repetition_index": 1,
- "repetitions": 0,
- "run_name": name,
- "run_type": "iteration",
- "threads": 1,
- "time_unit": "ns",
- }
- archery_result = {
- "counters": {"iterations": 47,
- "repetition_index": 1,
- "repetitions": 0,
- "run_name": name,
- "threads": 1},
- "name": name,
- "unit": "bytes_per_second",
- "less_is_better": False,
- "values": [1890209037.3405428],
- "time_unit": "ns",
- "times": [17458386.53190963],
- }
- assert "items_per_second" not in google_result
- assert "bytes_per_second" in google_result
- assert_benchmark(name, google_result, archery_result)
-
-
-def test_both_items_and_bytes_per_second():
- name = "ArrayArrayKernel<AddChecked, UInt8Type>/32768/0"
- google_result = {
- "bytes_per_second": 281772039.9844759,
- "cpu_time": 116292.58886653671,
- "items_per_second": 281772039.9844759,
- "iterations": 5964,
- "name": name,
- "null_percent": 0.0,
- "real_time": 119811.77313729875,
- "repetition_index": 0,
- "repetitions": 0,
- "run_name": name,
- "run_type": "iteration",
- "size": 32768.0,
- "threads": 1,
- "time_unit": "ns",
- }
- # Note that bytes_per_second trumps items_per_second
- archery_result = {
- "counters": {"iterations": 5964,
- "null_percent": 0.0,
- "repetition_index": 0,
- "repetitions": 0,
- "run_name": name,
- "threads": 1},
- "name": name,
- "unit": "bytes_per_second",
- "less_is_better": False,
- "values": [281772039.9844759],
- "time_unit": "ns",
- "times": [119811.77313729875],
- }
- assert "items_per_second" in google_result
- assert "bytes_per_second" in google_result
- assert_benchmark(name, google_result, archery_result)
-
-
-def test_neither_items_nor_bytes_per_second():
- name = "AllocateDeallocate<Jemalloc>/size:1048576/real_time"
- google_result = {
- "cpu_time": 1778.6004847419827,
- "iterations": 352765,
- "name": name,
- "real_time": 1835.3137357788837,
- "repetition_index": 0,
- "repetitions": 0,
- "run_name": name,
- "run_type": "iteration",
- "threads": 1,
- "time_unit": "ns",
- }
- archery_result = {
- "counters": {"iterations": 352765,
- "repetition_index": 0,
- "repetitions": 0,
- "run_name": name,
- "threads": 1},
- "name": name,
- "unit": "ns",
- "less_is_better": True,
- "values": [1835.3137357788837],
- "time_unit": "ns",
- "times": [1835.3137357788837],
- }
- assert "items_per_second" not in google_result
- assert "bytes_per_second" not in google_result
- assert_benchmark(name, google_result, archery_result)
-
-
-def test_prefer_real_time():
- name = "AllocateDeallocate<Jemalloc>/size:1048576/real_time"
- google_result = {
- "cpu_time": 1778.6004847419827,
- "iterations": 352765,
- "name": name,
- "real_time": 1835.3137357788837,
- "repetition_index": 0,
- "repetitions": 0,
- "run_name": name,
- "run_type": "iteration",
- "threads": 1,
- "time_unit": "ns",
- }
- archery_result = {
- "counters": {"iterations": 352765,
- "repetition_index": 0,
- "repetitions": 0,
- "run_name": name,
- "threads": 1},
- "name": name,
- "unit": "ns",
- "less_is_better": True,
- "values": [1835.3137357788837],
- "time_unit": "ns",
- "times": [1835.3137357788837],
- }
- assert name.endswith("/real_time")
- assert_benchmark(name, google_result, archery_result)
-
-
-def test_prefer_cpu_time():
- name = "AllocateDeallocate<Jemalloc>/size:1048576"
- google_result = {
- "cpu_time": 1778.6004847419827,
- "iterations": 352765,
- "name": name,
- "real_time": 1835.3137357788837,
- "repetition_index": 0,
- "repetitions": 0,
- "run_name": name,
- "run_type": "iteration",
- "threads": 1,
- "time_unit": "ns",
- }
- archery_result = {
- "counters": {"iterations": 352765,
- "repetition_index": 0,
- "repetitions": 0,
- "run_name": name,
- "threads": 1},
- "name": name,
- "unit": "ns",
- "less_is_better": True,
- "values": [1778.6004847419827],
- "time_unit": "ns",
- "times": [1835.3137357788837],
- }
- assert not name.endswith("/real_time")
- assert_benchmark(name, google_result, archery_result)
-
-
-def test_omits_aggregates():
- name = "AllocateDeallocate<Jemalloc>/size:1048576/real_time"
- google_aggregate = {
- "aggregate_name": "mean",
- "cpu_time": 1757.428694267678,
- "iterations": 3,
- "name": "AllocateDeallocate<Jemalloc>/size:1048576/real_time_mean",
- "real_time": 1849.3869337041162,
- "repetitions": 0,
- "run_name": name,
- "run_type": "aggregate",
- "threads": 1,
- "time_unit": "ns",
- }
- google_result = {
- "cpu_time": 1778.6004847419827,
- "iterations": 352765,
- "name": name,
- "real_time": 1835.3137357788837,
- "repetition_index": 0,
- "repetitions": 0,
- "run_name": name,
- "run_type": "iteration",
- "threads": 1,
- "time_unit": "ns",
- }
- archery_result = {
- "counters": {"iterations": 352765,
- "repetition_index": 0,
- "repetitions": 0,
- "run_name": name,
- "threads": 1},
- "name": name,
- "unit": "ns",
- "less_is_better": True,
- "values": [1835.3137357788837],
- "time_unit": "ns",
- "times": [1835.3137357788837],
- }
- assert google_aggregate["run_type"] == "aggregate"
- assert google_result["run_type"] == "iteration"
- observation1 = GoogleBenchmarkObservation(**google_aggregate)
- observation2 = GoogleBenchmarkObservation(**google_result)
- benchmark = GoogleBenchmark(name, [observation1, observation2])
- result = json.dumps(benchmark, cls=JsonEncoder)
- assert json.loads(result) == archery_result
diff --git a/dev/archery/archery/tests/test_bot.py b/dev/archery/archery/tests/test_bot.py
deleted file mode 100644
index e00853c..0000000
--- a/dev/archery/archery/tests/test_bot.py
+++ /dev/null
@@ -1,201 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-import json
-from unittest.mock import Mock
-
-import click
-import pytest
-import responses as rsps
-
-from archery.bot import CommentBot, CommandError, group
-
-
-@pytest.fixture
-def responses():
- with rsps.RequestsMock() as mock:
- yield mock
-
-
-def github_url(path):
- return 'https://api.github.com:443/{}'.format(path.strip('/'))
-
-
-@group()
-def custom_handler():
- pass
-
-
-@custom_handler.command()
-@click.pass_obj
-def extra(obj):
- return obj
-
-
-@custom_handler.command()
-@click.option('--force', '-f', is_flag=True)
-def build(force):
- return force
-
-
-@custom_handler.command()
-@click.option('--name', required=True)
-def benchmark(name):
- return name
-
-
-def test_click_based_commands():
- assert custom_handler('build') is False
- assert custom_handler('build -f') is True
-
- assert custom_handler('benchmark --name strings') == 'strings'
- with pytest.raises(CommandError):
- assert custom_handler('benchmark')
-
- assert custom_handler('extra', extra='data') == {'extra': 'data'}
-
-
-@pytest.mark.parametrize('fixture_name', [
- # the bot is not mentioned, nothing to do
- 'event-issue-comment-not-mentioning-ursabot.json',
- # don't respond to itself, it prevents recursive comment storms!
- 'event-issue-comment-by-ursabot.json',
- # non-authorized user sent the comment, do not respond
- 'event-issue-comment-by-non-authorized-user.json',
-])
-def test_noop_events(load_fixture, fixture_name):
- payload = load_fixture(fixture_name)
-
- handler = Mock()
- bot = CommentBot(name='ursabot', token='', handler=handler)
- bot.handle('issue_comment', payload)
-
- handler.assert_not_called()
-
-
-def test_issue_comment_without_pull_request(load_fixture, responses):
- responses.add(
- responses.GET,
- github_url('/repositories/169101701/issues/19'),
- json=load_fixture('issue-19.json'),
- status=200
- )
- responses.add(
- responses.GET,
- github_url('repos/ursa-labs/ursabot/pulls/19'),
- json={},
- status=404
- )
- responses.add(
- responses.POST,
- github_url('/repos/ursa-labs/ursabot/issues/19/comments'),
- json={}
- )
-
- def handler(command, **kwargs):
- pass
-
- payload = load_fixture('event-issue-comment-without-pull-request.json')
- bot = CommentBot(name='ursabot', token='', handler=handler)
- bot.handle('issue_comment', payload)
-
- post = responses.calls[2]
- assert json.loads(post.request.body) == {
- 'body': "The comment bot only listens to pull request comments!"
- }
-
-
-def test_respond_with_usage(load_fixture, responses):
- responses.add(
- responses.GET,
- github_url('/repositories/169101701/issues/26'),
- json=load_fixture('issue-26.json'),
- status=200
- )
- responses.add(
- responses.GET,
- github_url('/repos/ursa-labs/ursabot/pulls/26'),
- json=load_fixture('pull-request-26.json'),
- status=200
- )
- responses.add(
- responses.GET,
- github_url('/repos/ursa-labs/ursabot/issues/comments/480243811'),
- json=load_fixture('issue-comment-480243811.json')
- )
- responses.add(
- responses.POST,
- github_url('/repos/ursa-labs/ursabot/issues/26/comments'),
- json={}
- )
-
- def handler(command, **kwargs):
- raise CommandError('test-usage')
-
- payload = load_fixture('event-issue-comment-with-empty-command.json')
- bot = CommentBot(name='ursabot', token='', handler=handler)
- bot.handle('issue_comment', payload)
-
- post = responses.calls[3]
- assert json.loads(post.request.body) == {'body': '```\ntest-usage\n```'}
-
-
-@pytest.mark.parametrize(('command', 'reaction'), [
- ('@ursabot build', '+1'),
- ('@ursabot listen', '-1'),
-])
-def test_issue_comment_with_commands(load_fixture, responses, command,
- reaction):
- responses.add(
- responses.GET,
- github_url('/repositories/169101701/issues/26'),
- json=load_fixture('issue-26.json'),
- status=200
- )
- responses.add(
- responses.GET,
- github_url('/repos/ursa-labs/ursabot/pulls/26'),
- json=load_fixture('pull-request-26.json'),
- status=200
- )
- responses.add(
- responses.GET,
- github_url('/repos/ursa-labs/ursabot/issues/comments/480248726'),
- json=load_fixture('issue-comment-480248726.json')
- )
- responses.add(
- responses.POST,
- github_url(
- '/repos/ursa-labs/ursabot/issues/comments/480248726/reactions'
- ),
- json={}
- )
-
- def handler(command, **kwargs):
- if command == 'build':
- return True
- else:
- raise ValueError('Only `build` command is supported.')
-
- payload = load_fixture('event-issue-comment-build-command.json')
- payload["comment"]["body"] = command
-
- bot = CommentBot(name='ursabot', token='', handler=handler)
- bot.handle('issue_comment', payload)
-
- post = responses.calls[3]
- assert json.loads(post.request.body) == {'content': reaction}
diff --git a/dev/archery/archery/tests/test_cli.py b/dev/archery/archery/tests/test_cli.py
deleted file mode 100644
index b3199df..0000000
--- a/dev/archery/archery/tests/test_cli.py
+++ /dev/null
@@ -1,162 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-from unittest.mock import patch
-
-from click.testing import CliRunner
-
-from archery.cli import archery
-from archery.docker import DockerCompose
-
-
-@patch.object(DockerCompose, "pull")
-@patch.object(DockerCompose, "build")
-@patch.object(DockerCompose, "run")
-def test_docker_run_with_custom_command(run, build, pull):
- # with custom command
- args = ["docker", "run", "ubuntu-cpp", "bash"]
- result = CliRunner().invoke(archery, args)
- assert result.exit_code == 0
- pull.assert_called_once_with(
- "ubuntu-cpp", pull_leaf=True, using_docker=False
- )
- build.assert_called_once_with(
- "ubuntu-cpp",
- use_cache=True,
- use_leaf_cache=True,
- using_docker=False,
- using_buildx=False
- )
- run.assert_called_once_with(
- "ubuntu-cpp",
- command="bash",
- env={},
- user=None,
- using_docker=False,
- volumes=(),
- )
-
-
-@patch.object(DockerCompose, "pull")
-@patch.object(DockerCompose, "build")
-@patch.object(DockerCompose, "run")
-def test_docker_run_options(run, build, pull):
- # environment variables and volumes
- args = [
- "docker",
- "run",
- "-e",
- "ARROW_GANDIVA=OFF",
- "-e",
- "ARROW_FLIGHT=ON",
- "--volume",
- "./build:/build",
- "-v",
- "./ccache:/ccache:delegated",
- "-u",
- "root",
- "ubuntu-cpp",
- ]
- result = CliRunner().invoke(archery, args)
- assert result.exit_code == 0
- pull.assert_called_once_with(
- "ubuntu-cpp", pull_leaf=True, using_docker=False
- )
- build.assert_called_once_with(
- "ubuntu-cpp",
- use_cache=True,
- use_leaf_cache=True,
- using_docker=False,
- using_buildx=False
- )
- run.assert_called_once_with(
- "ubuntu-cpp",
- command=None,
- env={"ARROW_GANDIVA": "OFF", "ARROW_FLIGHT": "ON"},
- user="root",
- using_docker=False,
- volumes=(
- "./build:/build",
- "./ccache:/ccache:delegated",
- ),
- )
-
-
-@patch.object(DockerCompose, "run")
-def test_docker_run_without_pulling_or_building(run):
- args = ["docker", "run", "--no-pull", "--no-build", "ubuntu-cpp"]
- result = CliRunner().invoke(archery, args)
- assert result.exit_code == 0
- run.assert_called_once_with(
- "ubuntu-cpp",
- command=None,
- env={},
- user=None,
- using_docker=False,
- volumes=(),
- )
-
-
-@patch.object(DockerCompose, "pull")
-@patch.object(DockerCompose, "build")
-def test_docker_run_only_pulling_and_building(build, pull):
- args = ["docker", "run", "ubuntu-cpp", "--build-only"]
- result = CliRunner().invoke(archery, args)
- assert result.exit_code == 0
- pull.assert_called_once_with(
- "ubuntu-cpp", pull_leaf=True, using_docker=False
- )
- build.assert_called_once_with(
- "ubuntu-cpp",
- use_cache=True,
- use_leaf_cache=True,
- using_docker=False,
- using_buildx=False
- )
-
-
-@patch.object(DockerCompose, "build")
-@patch.object(DockerCompose, "run")
-def test_docker_run_without_build_cache(run, build):
- args = [
- "docker",
- "run",
- "--no-pull",
- "--force-build",
- "--user",
- "me",
- "--no-cache",
- "--no-leaf-cache",
- "ubuntu-cpp",
- ]
- result = CliRunner().invoke(archery, args)
- assert result.exit_code == 0
- build.assert_called_once_with(
- "ubuntu-cpp",
- use_cache=False,
- use_leaf_cache=False,
- using_docker=False,
- using_buildx=False
- )
- run.assert_called_once_with(
- "ubuntu-cpp",
- command=None,
- env={},
- user="me",
- using_docker=False,
- volumes=(),
- )
diff --git a/dev/archery/archery/tests/test_docker.py b/dev/archery/archery/tests/test_docker.py
deleted file mode 100644
index 09dcd27..0000000
--- a/dev/archery/archery/tests/test_docker.py
+++ /dev/null
@@ -1,512 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-import collections
-import os
-import re
-import subprocess
-from unittest import mock
-
-import pytest
-
-from archery.docker import DockerCompose
-from archery.testing import assert_subprocess_calls, override_env, PartialEnv
-
-
-missing_service_compose_yml = """
-version: '3.5'
-
-x-hierarchy:
- - foo:
- - sub-foo:
- - sub-sub-foo
- - another-sub-sub-foo
- - bar:
- - sub-bar
- - baz
-
-services:
- foo:
- image: org/foo
- sub-sub-foo:
- image: org/sub-sub-foo
- another-sub-sub-foo:
- image: org/another-sub-sub-foo
- bar:
- image: org/bar
- sub-bar:
- image: org/sub-bar
- baz:
- image: org/baz
-"""
-
-missing_node_compose_yml = """
-version: '3.5'
-
-x-hierarchy:
- - foo:
- - sub-foo:
- - sub-sub-foo
- - another-sub-sub-foo
- - bar
- - baz
-
-services:
- foo:
- image: org/foo
- sub-foo:
- image: org/sub-foo
- sub-sub-foo:
- image: org/sub-foo-foo
- another-sub-sub-foo:
- image: org/another-sub-sub-foo
- bar:
- image: org/bar
- sub-bar:
- image: org/sub-bar
- baz:
- image: org/baz
-"""
-
-ok_compose_yml = """
-version: '3.5'
-
-x-hierarchy:
- - foo:
- - sub-foo:
- - sub-sub-foo
- - another-sub-sub-foo
- - bar:
- - sub-bar
- - baz
-
-services:
- foo:
- image: org/foo
- sub-foo:
- image: org/sub-foo
- sub-sub-foo:
- image: org/sub-sub-foo
- another-sub-sub-foo:
- image: org/another-sub-sub-foo
- bar:
- image: org/bar
- sub-bar:
- image: org/sub-bar
- baz:
- image: org/baz
-"""
-
-arrow_compose_yml = """
-version: '3.5'
-
-x-with-gpus:
- - ubuntu-cuda
-
-x-hierarchy:
- - conda-cpp:
- - conda-python:
- - conda-python-pandas
- - conda-python-dask
- - ubuntu-cpp:
- - ubuntu-cpp-cmake32
- - ubuntu-c-glib:
- - ubuntu-ruby
- - ubuntu-cuda
-
-services:
- conda-cpp:
- image: org/conda-cpp
- build:
- context: .
- dockerfile: ci/docker/conda-cpp.dockerfile
- conda-python:
- image: org/conda-python
- build:
- context: .
- dockerfile: ci/docker/conda-cpp.dockerfile
- args:
- python: 3.6
- conda-python-pandas:
- image: org/conda-python-pandas
- build:
- context: .
- dockerfile: ci/docker/conda-python-pandas.dockerfile
- conda-python-dask:
- image: org/conda-python-dask
- ubuntu-cpp:
- image: org/ubuntu-cpp
- build:
- context: .
- dockerfile: ci/docker/ubuntu-${UBUNTU}-cpp.dockerfile
- ubuntu-cpp-cmake32:
- image: org/ubuntu-cpp-cmake32
- ubuntu-c-glib:
- image: org/ubuntu-c-glib
- ubuntu-ruby:
- image: org/ubuntu-ruby
- ubuntu-cuda:
- image: org/ubuntu-cuda
- environment:
- CUDA_ENV: 1
- OTHER_ENV: 2
- volumes:
- - /host:/container
- command: /bin/bash -c "echo 1 > /tmp/dummy && cat /tmp/dummy"
-"""
-
-arrow_compose_env = {
- 'UBUNTU': '20.04', # overridden below
- 'PYTHON': '3.6',
- 'PANDAS': 'latest',
- 'DASK': 'latest', # overridden below
-}
-
-
-def create_config(directory, yml_content, env_content=None):
- env_path = directory / '.env'
- config_path = directory / 'docker-compose.yml'
-
- with config_path.open('w') as fp:
- fp.write(yml_content)
-
- if env_content is not None:
- with env_path.open('w') as fp:
- for k, v in env_content.items():
- fp.write("{}={}\n".format(k, v))
-
- return config_path
-
-
-def format_run(args):
- cmd = ["run", "--rm"]
- if isinstance(args, str):
- return " ".join(cmd + [args])
- else:
- return cmd + args
-
-
-@pytest.fixture
-def arrow_compose_path(tmpdir):
- return create_config(tmpdir, arrow_compose_yml, arrow_compose_env)
-
-
-def test_config_validation(tmpdir):
- config_path = create_config(tmpdir, missing_service_compose_yml)
- msg = "`sub-foo` is defined in `x-hierarchy` bot not in `services`"
- with pytest.raises(ValueError, match=msg):
- DockerCompose(config_path)
-
- config_path = create_config(tmpdir, missing_node_compose_yml)
- msg = "`sub-bar` is defined in `services` but not in `x-hierarchy`"
- with pytest.raises(ValueError, match=msg):
- DockerCompose(config_path)
-
- config_path = create_config(tmpdir, ok_compose_yml)
- DockerCompose(config_path) # no issue
-
-
-def assert_docker_calls(compose, expected_args):
- base_command = ['docker']
- expected_commands = []
- for args in expected_args:
- if isinstance(args, str):
- args = re.split(r"\s", args)
- expected_commands.append(base_command + args)
- return assert_subprocess_calls(expected_commands, check=True)
-
-
-def assert_compose_calls(compose, expected_args, env=mock.ANY):
- base_command = ['docker-compose', '--file', str(compose.config.path)]
- expected_commands = []
- for args in expected_args:
- if isinstance(args, str):
- args = re.split(r"\s", args)
- expected_commands.append(base_command + args)
- return assert_subprocess_calls(expected_commands, check=True, env=env)
-
-
-def test_arrow_example_validation_passes(arrow_compose_path):
- DockerCompose(arrow_compose_path)
-
-
-def test_compose_default_params_and_env(arrow_compose_path):
- compose = DockerCompose(arrow_compose_path, params=dict(
- UBUNTU='18.04',
- DASK='master'
- ))
- assert compose.config.dotenv == arrow_compose_env
- assert compose.config.params == {
- 'UBUNTU': '18.04',
- 'DASK': 'master',
- }
-
-
-def test_forwarding_env_variables(arrow_compose_path):
- expected_calls = [
- "pull --ignore-pull-failures conda-cpp",
- "build conda-cpp",
- ]
- expected_env = PartialEnv(
- MY_CUSTOM_VAR_A='a',
- MY_CUSTOM_VAR_B='b'
- )
- with override_env({'MY_CUSTOM_VAR_A': 'a', 'MY_CUSTOM_VAR_B': 'b'}):
- compose = DockerCompose(arrow_compose_path)
- with assert_compose_calls(compose, expected_calls, env=expected_env):
- assert os.environ['MY_CUSTOM_VAR_A'] == 'a'
- assert os.environ['MY_CUSTOM_VAR_B'] == 'b'
- compose.pull('conda-cpp')
- compose.build('conda-cpp')
-
-
-def test_compose_pull(arrow_compose_path):
- compose = DockerCompose(arrow_compose_path)
-
- expected_calls = [
- "pull --ignore-pull-failures conda-cpp",
- ]
- with assert_compose_calls(compose, expected_calls):
- compose.clear_pull_memory()
- compose.pull('conda-cpp')
-
- expected_calls = [
- "pull --ignore-pull-failures conda-cpp",
- "pull --ignore-pull-failures conda-python",
- "pull --ignore-pull-failures conda-python-pandas"
- ]
- with assert_compose_calls(compose, expected_calls):
- compose.clear_pull_memory()
- compose.pull('conda-python-pandas')
-
- expected_calls = [
- "pull --ignore-pull-failures conda-cpp",
- "pull --ignore-pull-failures conda-python",
- ]
- with assert_compose_calls(compose, expected_calls):
- compose.clear_pull_memory()
- compose.pull('conda-python-pandas', pull_leaf=False)
-
-
-def test_compose_pull_params(arrow_compose_path):
- expected_calls = [
- "pull --ignore-pull-failures conda-cpp",
- "pull --ignore-pull-failures conda-python",
- ]
- compose = DockerCompose(arrow_compose_path, params=dict(UBUNTU='18.04'))
- expected_env = PartialEnv(PYTHON='3.6', PANDAS='latest')
- with assert_compose_calls(compose, expected_calls, env=expected_env):
- compose.clear_pull_memory()
- compose.pull('conda-python-pandas', pull_leaf=False)
-
-
-def test_compose_build(arrow_compose_path):
- compose = DockerCompose(arrow_compose_path)
-
- expected_calls = [
- "build conda-cpp",
- ]
- with assert_compose_calls(compose, expected_calls):
- compose.build('conda-cpp')
-
- expected_calls = [
- "build --no-cache conda-cpp"
- ]
- with assert_compose_calls(compose, expected_calls):
- compose.build('conda-cpp', use_cache=False)
-
- expected_calls = [
- "build conda-cpp",
- "build conda-python",
- "build conda-python-pandas"
- ]
- with assert_compose_calls(compose, expected_calls):
- compose.build('conda-python-pandas')
-
- expected_calls = [
- "build --no-cache conda-cpp",
- "build --no-cache conda-python",
- "build --no-cache conda-python-pandas",
- ]
- with assert_compose_calls(compose, expected_calls):
- compose.build('conda-python-pandas', use_cache=False)
-
- expected_calls = [
- "build conda-cpp",
- "build conda-python",
- "build --no-cache conda-python-pandas",
- ]
- with assert_compose_calls(compose, expected_calls):
- compose.build('conda-python-pandas', use_cache=True,
- use_leaf_cache=False)
-
-
-@mock.patch.dict(os.environ, {"BUILDKIT_INLINE_CACHE": "1"})
-def test_compose_buildkit_inline_cache(arrow_compose_path):
- compose = DockerCompose(arrow_compose_path)
-
- expected_calls = [
- "build --build-arg BUILDKIT_INLINE_CACHE=1 conda-cpp",
- ]
- with assert_compose_calls(compose, expected_calls):
- compose.build('conda-cpp')
-
-
-def test_compose_build_params(arrow_compose_path):
- expected_calls = [
- "build ubuntu-cpp",
- ]
-
- compose = DockerCompose(arrow_compose_path, params=dict(UBUNTU='18.04'))
- expected_env = PartialEnv(UBUNTU="18.04")
- with assert_compose_calls(compose, expected_calls, env=expected_env):
- compose.build('ubuntu-cpp')
-
- compose = DockerCompose(arrow_compose_path, params=dict(UBUNTU='16.04'))
- expected_env = PartialEnv(UBUNTU="16.04")
- with assert_compose_calls(compose, expected_calls, env=expected_env):
- compose.build('ubuntu-cpp')
-
- expected_calls = [
- "build --no-cache conda-cpp",
- "build --no-cache conda-python",
- "build --no-cache conda-python-pandas",
- ]
- compose = DockerCompose(arrow_compose_path, params=dict(UBUNTU='18.04'))
- expected_env = PartialEnv(PYTHON='3.6', PANDAS='latest')
- with assert_compose_calls(compose, expected_calls, env=expected_env):
- compose.build('conda-python-pandas', use_cache=False)
-
-
-def test_compose_run(arrow_compose_path):
- expected_calls = [
- format_run("conda-cpp"),
- ]
- compose = DockerCompose(arrow_compose_path)
- with assert_compose_calls(compose, expected_calls):
- compose.run('conda-cpp')
-
- expected_calls = [
- format_run("conda-python")
- ]
- expected_env = PartialEnv(PYTHON='3.6')
- with assert_compose_calls(compose, expected_calls, env=expected_env):
- compose.run('conda-python')
-
- compose = DockerCompose(arrow_compose_path, params=dict(PYTHON='3.8'))
- expected_env = PartialEnv(PYTHON='3.8')
- with assert_compose_calls(compose, expected_calls, env=expected_env):
- compose.run('conda-python')
-
- compose = DockerCompose(arrow_compose_path, params=dict(PYTHON='3.8'))
- for command in ["bash", "echo 1"]:
- expected_calls = [
- format_run(["conda-python", command]),
- ]
- expected_env = PartialEnv(PYTHON='3.8')
- with assert_compose_calls(compose, expected_calls, env=expected_env):
- compose.run('conda-python', command)
-
- expected_calls = [
- (
- format_run("-e CONTAINER_ENV_VAR_A=a -e CONTAINER_ENV_VAR_B=b "
- "conda-python")
- )
- ]
- compose = DockerCompose(arrow_compose_path)
- expected_env = PartialEnv(PYTHON='3.6')
- with assert_compose_calls(compose, expected_calls, env=expected_env):
- env = collections.OrderedDict([
- ("CONTAINER_ENV_VAR_A", "a"),
- ("CONTAINER_ENV_VAR_B", "b")
- ])
- compose.run('conda-python', env=env)
-
- expected_calls = [
- (
- format_run("--volume /host/build:/build --volume "
- "/host/ccache:/ccache:delegated conda-python")
- )
- ]
- compose = DockerCompose(arrow_compose_path)
- with assert_compose_calls(compose, expected_calls):
- volumes = ("/host/build:/build", "/host/ccache:/ccache:delegated")
- compose.run('conda-python', volumes=volumes)
-
-
-def test_compose_push(arrow_compose_path):
- compose = DockerCompose(arrow_compose_path, params=dict(PYTHON='3.8'))
- expected_env = PartialEnv(PYTHON="3.8")
- expected_calls = [
- mock.call(["docker", "login", "-u", "user", "-p", "pass"], check=True),
- ]
- for image in ["conda-cpp", "conda-python", "conda-python-pandas"]:
- expected_calls.append(
- mock.call(["docker-compose", "--file", str(compose.config.path),
- "push", image], check=True, env=expected_env)
- )
- with assert_subprocess_calls(expected_calls):
- compose.push('conda-python-pandas', user='user', password='pass')
-
-
-def test_compose_error(arrow_compose_path):
- compose = DockerCompose(arrow_compose_path, params=dict(
- PYTHON='3.8',
- PANDAS='master'
- ))
-
- error = subprocess.CalledProcessError(99, [])
- with mock.patch('subprocess.run', side_effect=error):
- with pytest.raises(RuntimeError) as exc:
- compose.run('conda-cpp')
-
- exception_message = str(exc.value)
- assert "exited with a non-zero exit code 99" in exception_message
- assert "PANDAS: latest" in exception_message
- assert "export PANDAS=master" in exception_message
-
-
-def test_image_with_gpu(arrow_compose_path):
- compose = DockerCompose(arrow_compose_path)
-
- expected_calls = [
- [
- "run", "--rm", "--gpus", "all",
- "-e", "CUDA_ENV=1",
- "-e", "OTHER_ENV=2",
- "-v", "/host:/container:rw",
- "org/ubuntu-cuda",
- '/bin/bash -c "echo 1 > /tmp/dummy && cat /tmp/dummy"'
- ]
- ]
- with assert_docker_calls(compose, expected_calls):
- compose.run('ubuntu-cuda')
-
-
-def test_listing_images(arrow_compose_path):
- compose = DockerCompose(arrow_compose_path)
- assert sorted(compose.images()) == [
- 'conda-cpp',
- 'conda-python',
- 'conda-python-dask',
- 'conda-python-pandas',
- 'ubuntu-c-glib',
- 'ubuntu-cpp',
- 'ubuntu-cpp-cmake32',
- 'ubuntu-cuda',
- 'ubuntu-ruby',
- ]
diff --git a/dev/archery/archery/tests/test_release.py b/dev/archery/archery/tests/test_release.py
deleted file mode 100644
index 75aac89..0000000
--- a/dev/archery/archery/tests/test_release.py
+++ /dev/null
@@ -1,333 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-import pytest
-
-from archery.release import (
- Release, MajorRelease, MinorRelease, PatchRelease,
- Jira, Version, Issue, CommitTitle, Commit
-)
-from archery.testing import DotDict
-
-
-# subset of issues per revision
-_issues = {
- "1.0.1": [
- Issue("ARROW-9684", type="Bug", summary="[C++] Title"),
- Issue("ARROW-9667", type="New Feature", summary="[Crossbow] Title"),
- Issue("ARROW-9659", type="Bug", summary="[C++] Title"),
- Issue("ARROW-9644", type="Bug", summary="[C++][Dataset] Title"),
- Issue("ARROW-9643", type="Bug", summary="[C++] Title"),
- Issue("ARROW-9609", type="Bug", summary="[C++] Title"),
- Issue("ARROW-9606", type="Bug", summary="[C++][Dataset] Title")
- ],
- "1.0.0": [
- Issue("ARROW-300", type="New Feature", summary="[Format] Title"),
- Issue("ARROW-4427", type="Task", summary="[Doc] Title"),
- Issue("ARROW-5035", type="Improvement", summary="[C#] Title"),
- Issue("ARROW-8473", type="Bug", summary="[Rust] Title"),
- Issue("ARROW-8472", type="Bug", summary="[Go][Integration] Title"),
- Issue("ARROW-8471", type="Bug", summary="[C++][Integration] Title"),
- Issue("ARROW-8974", type="Improvement", summary="[C++] Title"),
- Issue("ARROW-8973", type="New Feature", summary="[Java] Title")
- ],
- "0.17.1": [
- Issue("ARROW-8684", type="Bug", summary="[Python] Title"),
- Issue("ARROW-8657", type="Bug", summary="[C++][Parquet] Title"),
- Issue("ARROW-8641", type="Bug", summary="[Python] Title"),
- Issue("ARROW-8609", type="Bug", summary="[C++] Title"),
- ],
- "0.17.0": [
- Issue("ARROW-2882", type="New Feature", summary="[C++][Python] Title"),
- Issue("ARROW-2587", type="Bug", summary="[Python] Title"),
- Issue("ARROW-2447", type="Improvement", summary="[C++] Title"),
- Issue("ARROW-2255", type="Bug", summary="[Integration] Title"),
- Issue("ARROW-1907", type="Bug", summary="[C++/Python] Title"),
- Issue("ARROW-1636", type="New Feature", summary="[Format] Title")
- ]
-}
-
-
-class FakeJira(Jira):
-
- def __init__(self):
- pass
-
- def project_versions(self, project='ARROW'):
- return [
- Version.parse("3.0.0", released=False),
- Version.parse("2.0.0", released=False),
- Version.parse("1.1.0", released=False),
- Version.parse("1.0.1", released=False),
- Version.parse("1.0.0", released=True),
- Version.parse("0.17.1", released=True),
- Version.parse("0.17.0", released=True),
- Version.parse("0.16.0", released=True),
- Version.parse("0.15.2", released=True),
- Version.parse("0.15.1", released=True),
- Version.parse("0.15.0", released=True),
- ]
-
- def project_issues(self, version, project='ARROW'):
- return _issues[str(version)]
-
-
-@pytest.fixture
-def fake_jira():
- return FakeJira()
-
-
-def test_version(fake_jira):
- v = Version.parse("1.2.5")
- assert str(v) == "1.2.5"
- assert v.major == 1
- assert v.minor == 2
- assert v.patch == 5
- assert v.released is False
- assert v.release_date is None
-
- v = Version.parse("1.0.0", released=True, release_date="2020-01-01")
- assert str(v) == "1.0.0"
- assert v.major == 1
- assert v.minor == 0
- assert v.patch == 0
- assert v.released is True
- assert v.release_date == "2020-01-01"
-
-
-def test_issue(fake_jira):
- i = Issue("ARROW-1234", type='Bug', summary="title")
- assert i.key == "ARROW-1234"
- assert i.type == "Bug"
- assert i.summary == "title"
- assert i.project == "ARROW"
- assert i.number == 1234
-
- i = Issue("PARQUET-1111", type='Improvement', summary="another title")
- assert i.key == "PARQUET-1111"
- assert i.type == "Improvement"
- assert i.summary == "another title"
- assert i.project == "PARQUET"
- assert i.number == 1111
-
- fake_jira_issue = DotDict({
- 'key': 'ARROW-2222',
- 'fields': {
- 'issuetype': {
- 'name': 'Feature'
- },
- 'summary': 'Issue title'
- }
- })
- i = Issue.from_jira(fake_jira_issue)
- assert i.key == "ARROW-2222"
- assert i.type == "Feature"
- assert i.summary == "Issue title"
- assert i.project == "ARROW"
- assert i.number == 2222
-
-
-def test_commit_title():
- t = CommitTitle.parse(
- "ARROW-9598: [C++][Parquet] Fix writing nullable structs"
- )
- assert t.project == "ARROW"
- assert t.issue == "ARROW-9598"
- assert t.components == ["C++", "Parquet"]
- assert t.summary == "Fix writing nullable structs"
-
- t = CommitTitle.parse(
- "ARROW-8002: [C++][Dataset][R] Support partitioned dataset writing"
- )
- assert t.project == "ARROW"
- assert t.issue == "ARROW-8002"
- assert t.components == ["C++", "Dataset", "R"]
- assert t.summary == "Support partitioned dataset writing"
-
- t = CommitTitle.parse(
- "ARROW-9600: [Rust][Arrow] pin older version of proc-macro2 during "
- "build"
- )
- assert t.project == "ARROW"
- assert t.issue == "ARROW-9600"
- assert t.components == ["Rust", "Arrow"]
- assert t.summary == "pin older version of proc-macro2 during build"
-
- t = CommitTitle.parse("[Release] Update versions for 1.0.0")
- assert t.project is None
- assert t.issue is None
- assert t.components == ["Release"]
- assert t.summary == "Update versions for 1.0.0"
-
- t = CommitTitle.parse("[Python][Doc] Fix rst role dataset.rst (#7725)")
- assert t.project is None
- assert t.issue is None
- assert t.components == ["Python", "Doc"]
- assert t.summary == "Fix rst role dataset.rst (#7725)"
-
- t = CommitTitle.parse(
- "PARQUET-1882: [C++] Buffered Reads should allow for 0 length"
- )
- assert t.project == 'PARQUET'
- assert t.issue == 'PARQUET-1882'
- assert t.components == ["C++"]
- assert t.summary == "Buffered Reads should allow for 0 length"
-
- t = CommitTitle.parse(
- "ARROW-9340 [R] Use CRAN version of decor package "
- "\nsomething else\n"
- "\nwhich should be truncated"
- )
- assert t.project == 'ARROW'
- assert t.issue == 'ARROW-9340'
- assert t.components == ["R"]
- assert t.summary == "Use CRAN version of decor package "
-
-
-def test_release_basics(fake_jira):
- r = Release.from_jira("1.0.0", jira=fake_jira)
- assert isinstance(r, MajorRelease)
- assert r.is_released is True
- assert r.branch == 'master'
- assert r.tag == 'apache-arrow-1.0.0'
-
- r = Release.from_jira("1.1.0", jira=fake_jira)
- assert isinstance(r, MinorRelease)
- assert r.is_released is False
- assert r.branch == 'maint-1.x.x'
- assert r.tag == 'apache-arrow-1.1.0'
-
- # minor releases before 1.0 are treated as major releases
- r = Release.from_jira("0.17.0", jira=fake_jira)
- assert isinstance(r, MajorRelease)
- assert r.is_released is True
- assert r.branch == 'master'
- assert r.tag == 'apache-arrow-0.17.0'
-
- r = Release.from_jira("0.17.1", jira=fake_jira)
- assert isinstance(r, PatchRelease)
- assert r.is_released is True
- assert r.branch == 'maint-0.17.x'
- assert r.tag == 'apache-arrow-0.17.1'
-
-
-def test_previous_and_next_release(fake_jira):
- r = Release.from_jira("3.0.0", jira=fake_jira)
- assert isinstance(r.previous, MajorRelease)
- assert r.previous.version == Version.parse("2.0.0")
- with pytest.raises(ValueError, match="There is no upcoming release set"):
- assert r.next
-
- r = Release.from_jira("2.0.0", jira=fake_jira)
- assert isinstance(r.previous, MajorRelease)
- assert isinstance(r.next, MajorRelease)
- assert r.previous.version == Version.parse("1.0.0")
- assert r.next.version == Version.parse("3.0.0")
-
- r = Release.from_jira("1.1.0", jira=fake_jira)
- assert isinstance(r.previous, MajorRelease)
- assert isinstance(r.next, MajorRelease)
- assert r.previous.version == Version.parse("1.0.0")
- assert r.next.version == Version.parse("2.0.0")
-
- r = Release.from_jira("1.0.0", jira=fake_jira)
- assert isinstance(r.next, MajorRelease)
- assert isinstance(r.previous, MajorRelease)
- assert r.previous.version == Version.parse("0.17.0")
- assert r.next.version == Version.parse("2.0.0")
-
- r = Release.from_jira("0.17.0", jira=fake_jira)
- assert isinstance(r.previous, MajorRelease)
- assert r.previous.version == Version.parse("0.16.0")
-
- r = Release.from_jira("0.15.2", jira=fake_jira)
- assert isinstance(r.previous, PatchRelease)
- assert isinstance(r.next, MajorRelease)
- assert r.previous.version == Version.parse("0.15.1")
- assert r.next.version == Version.parse("0.16.0")
-
- r = Release.from_jira("0.15.1", jira=fake_jira)
- assert isinstance(r.previous, MajorRelease)
- assert isinstance(r.next, PatchRelease)
- assert r.previous.version == Version.parse("0.15.0")
- assert r.next.version == Version.parse("0.15.2")
-
-
-def test_release_issues(fake_jira):
- # major release issues
- r = Release.from_jira("1.0.0", jira=fake_jira)
- assert r.issues.keys() == set([
- "ARROW-300",
- "ARROW-4427",
- "ARROW-5035",
- "ARROW-8473",
- "ARROW-8472",
- "ARROW-8471",
- "ARROW-8974",
- "ARROW-8973"
- ])
- # minor release issues
- r = Release.from_jira("0.17.0", jira=fake_jira)
- assert r.issues.keys() == set([
- "ARROW-2882",
- "ARROW-2587",
- "ARROW-2447",
- "ARROW-2255",
- "ARROW-1907",
- "ARROW-1636",
- ])
- # patch release issues
- r = Release.from_jira("1.0.1", jira=fake_jira)
- assert r.issues.keys() == set([
- "ARROW-9684",
- "ARROW-9667",
- "ARROW-9659",
- "ARROW-9644",
- "ARROW-9643",
- "ARROW-9609",
- "ARROW-9606"
- ])
-
-
-@pytest.mark.parametrize(('version', 'ncommits'), [
- ("1.0.0", 771),
- ("0.17.1", 27),
- ("0.17.0", 569),
- ("0.15.1", 41)
-])
-def test_release_commits(fake_jira, version, ncommits):
- r = Release.from_jira(version, jira=fake_jira)
- assert len(r.commits) == ncommits
- for c in r.commits:
- assert isinstance(c, Commit)
- assert isinstance(c.title, CommitTitle)
- assert c.url.endswith(c.hexsha)
-
-
-def test_maintenance_patch_selection(fake_jira):
- r = Release.from_jira("0.17.1", jira=fake_jira)
-
- shas_to_pick = [
- c.hexsha for c in r.commits_to_pick(exclude_already_applied=False)
- ]
- expected = [
- '8939b4bd446ee406d5225c79d563a27d30fd7d6d',
- 'bcef6c95a324417e85e0140f9745d342cd8784b3',
- '6002ec388840de5622e39af85abdc57a2cccc9b2',
- '9123dadfd123bca7af4eaa9455f5b0d1ca8b929d',
- ]
- assert shas_to_pick == expected
diff --git a/dev/archery/archery/tests/test_testing.py b/dev/archery/archery/tests/test_testing.py
deleted file mode 100644
index 117b928..0000000
--- a/dev/archery/archery/tests/test_testing.py
+++ /dev/null
@@ -1,62 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-import subprocess
-
-import pytest
-
-from archery.testing import PartialEnv, assert_subprocess_calls
-
-
-def test_partial_env():
- assert PartialEnv(a=1, b=2) == {'a': 1, 'b': 2, 'c': 3}
- assert PartialEnv(a=1) == {'a': 1, 'b': 2, 'c': 3}
- assert PartialEnv(a=1, b=2) == {'a': 1, 'b': 2}
- assert PartialEnv(a=1, b=2) != {'b': 2, 'c': 3}
- assert PartialEnv(a=1, b=2) != {'a': 1, 'c': 3}
-
-
-def test_assert_subprocess_calls():
- expected_calls = [
- "echo Hello",
- ["echo", "World"]
- ]
- with assert_subprocess_calls(expected_calls):
- subprocess.run(['echo', 'Hello'])
- subprocess.run(['echo', 'World'])
-
- expected_env = PartialEnv(
- CUSTOM_ENV_A='a',
- CUSTOM_ENV_C='c'
- )
- with assert_subprocess_calls(expected_calls, env=expected_env):
- env = {
- 'CUSTOM_ENV_A': 'a',
- 'CUSTOM_ENV_B': 'b',
- 'CUSTOM_ENV_C': 'c'
- }
- subprocess.run(['echo', 'Hello'], env=env)
- subprocess.run(['echo', 'World'], env=env)
-
- with pytest.raises(AssertionError):
- with assert_subprocess_calls(expected_calls, env=expected_env):
- env = {
- 'CUSTOM_ENV_B': 'b',
- 'CUSTOM_ENV_C': 'c'
- }
- subprocess.run(['echo', 'Hello'], env=env)
- subprocess.run(['echo', 'World'], env=env)
diff --git a/dev/archery/archery/utils/__init__.py b/dev/archery/archery/utils/__init__.py
deleted file mode 100644
index 13a8339..0000000
--- a/dev/archery/archery/utils/__init__.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
diff --git a/dev/archery/archery/utils/cache.py b/dev/archery/archery/utils/cache.py
deleted file mode 100644
index d92c5f3..0000000
--- a/dev/archery/archery/utils/cache.py
+++ /dev/null
@@ -1,80 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-from pathlib import Path
-import os
-from urllib.request import urlopen
-
-from .logger import logger
-
-ARCHERY_CACHE_DIR = Path.home() / ".cache" / "archery"
-
-
-class Cache:
- """ Cache stores downloaded objects, notably apache-rat.jar. """
-
- def __init__(self, path=ARCHERY_CACHE_DIR):
- self.root = path
-
- if not path.exists():
- os.makedirs(path)
-
- def key_path(self, key):
- """ Return the full path of a key. """
- return self.root/key
-
- def get(self, key):
- """ Return the full path of a key if cached, None otherwise. """
- path = self.key_path(key)
- return path if path.exists() else None
-
- def delete(self, key):
- """ Remove a key (and the file) from the cache. """
- path = self.get(key)
- if path:
- path.unlink()
-
- def get_or_insert(self, key, create):
- """
- Get or Insert a key from the cache. If the key is not found, the
- `create` closure will be evaluated.
-
- The `create` closure takes a single parameter, the path where the
- object should be store. The file should only be created upon success.
- """
- path = self.key_path(key)
-
- if not path.exists():
- create(path)
-
- return path
-
- def get_or_insert_from_url(self, key, url):
- """
- Get or Insert a key from the cache. If the key is not found, the file
- is downloaded from `url`.
- """
- def download(path):
- """ Tiny wrapper that download a file and save as key. """
- logger.debug("Downloading {} as {}".format(url, path))
- conn = urlopen(url)
- # Ensure the download is completed before writing to disks.
- content = conn.read()
- with open(path, "wb") as path_fd:
- path_fd.write(content)
-
- return self.get_or_insert(key, download)
diff --git a/dev/archery/archery/utils/cmake.py b/dev/archery/archery/utils/cmake.py
deleted file mode 100644
index f93895b..0000000
--- a/dev/archery/archery/utils/cmake.py
+++ /dev/null
@@ -1,215 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-import os
-import re
-from shutil import rmtree, which
-
-from .command import Command, default_bin
-
-
-class CMake(Command):
- def __init__(self, cmake_bin=None):
- self.bin = default_bin(cmake_bin, "cmake")
-
- @staticmethod
- def default_generator():
- """ Infer default generator.
-
- Gives precedence to ninja if there exists an executable named `ninja`
- in the search path.
- """
- found_ninja = which("ninja")
- return "Ninja" if found_ninja else "Unix Makefiles"
-
-
-cmake = CMake()
-
-
-class CMakeDefinition:
- """ CMakeDefinition captures the cmake invocation arguments.
-
- It allows creating build directories with the same definition, e.g.
- ```
- build_1 = cmake_def.build("/tmp/build-1")
- build_2 = cmake_def.build("/tmp/build-2")
-
- ...
-
- build1.all()
- build2.all()
- """
-
- def __init__(self, source, build_type="release", generator=None,
- definitions=None, env=None):
- """ Initialize a CMakeDefinition
-
- Parameters
- ----------
- source : str
- Source directory where the top-level CMakeLists.txt is
- located. This is usually the root of the project.
- generator : str, optional
- definitions: list(str), optional
- env : dict(str,str), optional
- Environment to use when invoking cmake. This can be required to
- work around cmake deficiencies, e.g. CC and CXX.
- """
- self.source = os.path.abspath(source)
- self.build_type = build_type
- self.generator = generator if generator else cmake.default_generator()
- self.definitions = definitions if definitions else []
- self.env = env
-
- @property
- def arguments(self):
- """" Return the arguments to cmake invocation. """
- arguments = [
- "-G{}".format(self.generator),
- ] + self.definitions + [
- self.source
- ]
- return arguments
-
- def build(self, build_dir, force=False, cmd_kwargs=None, **kwargs):
- """ Invoke cmake into a build directory.
-
- Parameters
- ----------
- build_dir : str
- Directory in which the CMake build will be instantiated.
- force : bool
- If the build folder exists, delete it before. Otherwise if it's
- present, an error will be returned.
- """
- if os.path.exists(build_dir):
- # Extra safety to ensure we're deleting a build folder.
- if not CMakeBuild.is_build_dir(build_dir):
- raise FileExistsError(
- "{} is not a cmake build".format(build_dir)
- )
- if not force:
- raise FileExistsError(
- "{} exists use force=True".format(build_dir)
- )
- rmtree(build_dir)
-
- os.mkdir(build_dir)
-
- cmd_kwargs = cmd_kwargs if cmd_kwargs else {}
- cmake(*self.arguments, cwd=build_dir, env=self.env, **cmd_kwargs)
- return CMakeBuild(build_dir, self.build_type, definition=self,
- **kwargs)
-
- def __repr__(self):
- return "CMakeDefinition[source={}]".format(self.source)
-
-
-CMAKE_BUILD_TYPE_RE = re.compile("CMAKE_BUILD_TYPE:STRING=([a-zA-Z]+)")
-
-
-class CMakeBuild(CMake):
- """ CMakeBuild represents a build directory initialized by cmake.
-
- The build instance can be used to build/test/install. It alleviates the
- user to know which generator is used.
- """
-
- def __init__(self, build_dir, build_type, definition=None):
- """ Initialize a CMakeBuild.
-
- The caller must ensure that cmake was invoked in the build directory.
-
- Parameters
- ----------
- definition : CMakeDefinition
- The definition to build from.
- build_dir : str
- The build directory to setup into.
- """
- assert CMakeBuild.is_build_dir(build_dir)
- super().__init__()
- self.build_dir = os.path.abspath(build_dir)
- self.build_type = build_type
- self.definition = definition
-
- @property
- def binaries_dir(self):
- return os.path.join(self.build_dir, self.build_type)
-
- def run(self, *argv, verbose=False, **kwargs):
- cmake_args = ["--build", self.build_dir, "--"]
- extra = []
- if verbose:
- extra.append("-v" if self.bin.endswith("ninja") else "VERBOSE=1")
- # Commands must be ran under the build directory
- return super().run(*cmake_args, *extra,
- *argv, **kwargs, cwd=self.build_dir)
-
- def all(self):
- return self.run("all")
-
- def clean(self):
- return self.run("clean")
-
- def install(self):
- return self.run("install")
-
- def test(self):
- return self.run("test")
-
- @staticmethod
- def is_build_dir(path):
- """ Indicate if a path is CMake build directory.
-
- This method only checks for the existence of paths and does not do any
- validation whatsoever.
- """
- cmake_cache = os.path.join(path, "CMakeCache.txt")
- cmake_files = os.path.join(path, "CMakeFiles")
- return os.path.exists(cmake_cache) and os.path.exists(cmake_files)
-
- @staticmethod
- def from_path(path):
- """ Instantiate a CMakeBuild from a path.
-
- This is used to recover from an existing physical directory (created
- with or without CMakeBuild).
-
- Note that this method is not idempotent as the original definition will
- be lost. Only build_type is recovered.
- """
- if not CMakeBuild.is_build_dir(path):
- raise ValueError("Not a valid CMakeBuild path: {}".format(path))
-
- build_type = None
- # Infer build_type by looking at CMakeCache.txt and looking for a magic
- # definition
- cmake_cache_path = os.path.join(path, "CMakeCache.txt")
- with open(cmake_cache_path, "r") as cmake_cache:
- candidates = CMAKE_BUILD_TYPE_RE.findall(cmake_cache.read())
- build_type = candidates[0].lower() if candidates else "release"
-
- return CMakeBuild(path, build_type)
-
- def __repr__(self):
- return ("CMakeBuild["
- "build = {},"
- "build_type = {},"
- "definition = {}]".format(self.build_dir,
- self.build_type,
- self.definition))
diff --git a/dev/archery/archery/utils/command.py b/dev/archery/archery/utils/command.py
deleted file mode 100644
index 84d2842..0000000
--- a/dev/archery/archery/utils/command.py
+++ /dev/null
@@ -1,97 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-import os
-import shlex
-import shutil
-import subprocess
-
-from .logger import logger, ctx
-
-
-def default_bin(name, default):
- assert(default)
- env_name = "ARCHERY_{0}_BIN".format(default.upper())
- return name if name else os.environ.get(env_name, default)
-
-
-# Decorator running a command and returning stdout
-class capture_stdout:
- def __init__(self, strip=False, listify=False):
- self.strip = strip
- self.listify = listify
-
- def __call__(self, f):
- def strip_it(x):
- return x.strip() if self.strip else x
-
- def list_it(x):
- return x.decode('utf-8').splitlines() if self.listify else x
-
- def wrapper(*argv, **kwargs):
- # Ensure stdout is captured
- kwargs["stdout"] = subprocess.PIPE
- return list_it(strip_it(f(*argv, **kwargs).stdout))
- return wrapper
-
-
-class Command:
- """ A runnable command.
-
- Class inheriting from the Command class must provide the bin
- property/attribute.
- """
-
- def __init__(self, bin):
- self.bin = bin
-
- def run(self, *argv, **kwargs):
- assert hasattr(self, "bin")
- invocation = shlex.split(self.bin)
- invocation.extend(argv)
-
- for key in ["stdout", "stderr"]:
- # Preserve caller intention, otherwise silence
- if key not in kwargs and ctx.quiet:
- kwargs[key] = subprocess.PIPE
-
- # Prefer safe by default
- if "check" not in kwargs:
- kwargs["check"] = True
-
- logger.debug("Executing `{}`".format(invocation))
- return subprocess.run(invocation, **kwargs)
-
- @property
- def available(self):
- """ Indicate if the command binary is found in PATH. """
- binary = shlex.split(self.bin)[0]
- return shutil.which(binary) is not None
-
- def __call__(self, *argv, **kwargs):
- return self.run(*argv, **kwargs)
-
-
-class CommandStackMixin:
- def run(self, *argv, **kwargs):
- stacked_args = self.argv + argv
- return super(CommandStackMixin, self).run(*stacked_args, **kwargs)
-
-
-class Bash(Command):
- def __init__(self, bash_bin=None):
- self.bin = default_bin(bash_bin, "bash")
diff --git a/dev/archery/archery/utils/git.py b/dev/archery/archery/utils/git.py
deleted file mode 100644
index 798bc5d..0000000
--- a/dev/archery/archery/utils/git.py
+++ /dev/null
@@ -1,100 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-from .command import Command, capture_stdout, default_bin
-from ..compat import _stringify_path
-
-
-# Decorator prepending argv with the git sub-command found with the method
-# name.
-def git_cmd(fn):
- # function name is the subcommand
- sub_cmd = fn.__name__.replace("_", "-")
-
- def wrapper(self, *argv, **kwargs):
- return fn(self, sub_cmd, *argv, **kwargs)
- return wrapper
-
-
-class Git(Command):
- def __init__(self, git_bin=None):
- self.bin = default_bin(git_bin, "git")
-
- def run_cmd(self, cmd, *argv, git_dir=None, **kwargs):
- """ Inject flags before sub-command in argv. """
- opts = []
- if git_dir is not None:
- opts.extend(["-C", _stringify_path(git_dir)])
-
- return self.run(*opts, cmd, *argv, **kwargs)
-
- @capture_stdout(strip=False)
- @git_cmd
- def archive(self, *argv, **kwargs):
- return self.run_cmd(*argv, **kwargs)
-
- @git_cmd
- def clone(self, *argv, **kwargs):
- return self.run_cmd(*argv, **kwargs)
-
- @git_cmd
- def fetch(self, *argv, **kwargs):
- return self.run_cmd(*argv, **kwargs)
-
- @git_cmd
- def checkout(self, *argv, **kwargs):
- return self.run_cmd(*argv, **kwargs)
-
- def dirty(self, **kwargs):
- return len(self.status("--short", **kwargs)) > 0
-
- @git_cmd
- def log(self, *argv, **kwargs):
- return self.run_cmd(*argv, **kwargs)
-
- @capture_stdout(strip=True, listify=True)
- @git_cmd
- def ls_files(self, *argv, listify=False, **kwargs):
- stdout = self.run_cmd(*argv, **kwargs)
- return stdout
-
- @capture_stdout(strip=True)
- @git_cmd
- def rev_parse(self, *argv, **kwargs):
- return self.run_cmd(*argv, **kwargs)
-
- @capture_stdout(strip=True)
- @git_cmd
- def status(self, *argv, **kwargs):
- return self.run_cmd(*argv, **kwargs)
-
- @capture_stdout(strip=True)
- def head(self, **kwargs):
- """ Return commit pointed by HEAD. """
- return self.rev_parse("HEAD", **kwargs)
-
- @capture_stdout(strip=True)
- def current_branch(self, **kwargs):
- return self.rev_parse("--abbrev-ref", "HEAD", **kwargs)
-
- def repository_root(self, git_dir=None, **kwargs):
- """ Locates the repository's root path from a subdirectory. """
- stdout = self.rev_parse("--show-toplevel", git_dir=git_dir, **kwargs)
- return stdout.decode('utf-8')
-
-
-git = Git()
diff --git a/dev/archery/archery/utils/lint.py b/dev/archery/archery/utils/lint.py
deleted file mode 100644
index e81d6ac..0000000
--- a/dev/archery/archery/utils/lint.py
+++ /dev/null
@@ -1,383 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-import gzip
-import os
-from pathlib import Path
-
-import click
-
-from .command import Bash, Command, default_bin
-from .cmake import CMake
-from .git import git
-from .logger import logger
-from ..lang.cpp import CppCMakeDefinition, CppConfiguration
-from ..lang.rust import Cargo
-from ..lang.python import Autopep8, Flake8, NumpyDoc
-from .rat import Rat, exclusion_from_globs
-from .tmpdir import tmpdir
-
-
-class LintValidationException(Exception):
- pass
-
-
-class LintResult:
- def __init__(self, success, reason=None):
- self.success = success
-
- def ok(self):
- if not self.success:
- raise LintValidationException
-
- @staticmethod
- def from_cmd(command_result):
- return LintResult(command_result.returncode == 0)
-
-
-def cpp_linter(src, build_dir, clang_format=True, cpplint=True,
- clang_tidy=False, iwyu=False, iwyu_all=False,
- fix=False):
- """ Run clang-format, cpplint and clang-tidy on cpp/ codebase. """
- logger.info("Running C++ linters")
-
- cmake = CMake()
- if not cmake.available:
- logger.error("cpp linter requested but cmake binary not found.")
- return
-
- # A cmake build directory is required to populate `compile_commands.json`
- # which in turn is required by clang-tidy. It also provides a convenient
- # way to hide clang-format/clang-tidy invocation via the Generate
- # (ninja/make) targets.
-
- # ARROW_LINT_ONLY exits early but ignore building compile_command.json
- lint_only = not (iwyu or clang_tidy)
- cmake_args = {"with_python": False, "with_lint_only": lint_only}
- cmake_def = CppCMakeDefinition(src.cpp, CppConfiguration(**cmake_args))
-
- build = cmake_def.build(build_dir)
- if clang_format:
- target = "format" if fix else "check-format"
- yield LintResult.from_cmd(build.run(target, check=False))
-
- if cpplint:
- yield LintResult.from_cmd(build.run("lint", check=False))
- yield LintResult.from_cmd(build.run("lint_cpp_cli", check=False))
-
- if clang_tidy:
- yield LintResult.from_cmd(build.run("check-clang-tidy", check=False))
-
- if iwyu:
- if iwyu_all:
- iwyu_cmd = "iwyu-all"
- else:
- iwyu_cmd = "iwyu"
- yield LintResult.from_cmd(build.run(iwyu_cmd, check=False))
-
-
-class CMakeFormat(Command):
- def __init__(self, cmake_format_bin):
- self.bin = cmake_format_bin
-
-
-def cmake_linter(src, fix=False):
- """ Run cmake-format.py on all CMakeFiles.txt """
- logger.info("Running cmake-format linters")
-
- if not fix:
- logger.warn("run-cmake-format modifies files, regardless of --fix")
-
- arrow_cmake_format = os.path.join(src.path, "run-cmake-format.py")
- cmake_format = CMakeFormat(cmake_format_bin=arrow_cmake_format)
- yield LintResult.from_cmd(cmake_format("--check"))
-
-
-def python_linter(src, fix=False):
- """Run Python linters on python/pyarrow, python/examples, setup.py
- and dev/. """
- setup_py = os.path.join(src.python, "setup.py")
- setup_cfg = os.path.join(src.python, "setup.cfg")
-
- logger.info("Running Python formatter (autopep8)")
-
- autopep8 = Autopep8()
- if not autopep8.available:
- logger.error(
- "Python formatter requested but autopep8 binary not found. "
- "Please run `pip install -r dev/archery/requirements-lint.txt`")
- return
-
- # Gather files for autopep8
- patterns = ["python/pyarrow/**/*.py",
- "python/pyarrow/**/*.pyx",
- "python/pyarrow/**/*.pxd",
- "python/pyarrow/**/*.pxi",
- "python/examples/**/*.py",
- "dev/archery/**/*.py",
- ]
- files = [setup_py]
- for pattern in patterns:
- files += list(map(str, Path(src.path).glob(pattern)))
-
- args = ['--global-config', setup_cfg, '--ignore-local-config']
- if fix:
- args += ['-j0', '--in-place']
- args += sorted(files)
- yield LintResult.from_cmd(autopep8(*args))
- else:
- # XXX `-j0` doesn't work well with `--exit-code`, so instead
- # we capture the diff and check whether it's empty
- # (https://github.com/hhatto/autopep8/issues/543)
- args += ['-j0', '--diff']
- args += sorted(files)
- diff = autopep8.run_captured(*args)
- if diff:
- print(diff.decode('utf8'))
- yield LintResult(success=False)
- else:
- yield LintResult(success=True)
-
- # Run flake8 after autopep8 (the latter may have modified some files)
- logger.info("Running Python linter (flake8)")
-
- flake8 = Flake8()
- if not flake8.available:
- logger.error(
- "Python linter requested but flake8 binary not found. "
- "Please run `pip install -r dev/archery/requirements-lint.txt`")
- return
-
- flake8_exclude = ['.venv*']
-
- yield LintResult.from_cmd(
- flake8("--extend-exclude=" + ','.join(flake8_exclude),
- setup_py, src.pyarrow, os.path.join(src.python, "examples"),
- src.dev, check=False))
- config = os.path.join(src.python, ".flake8.cython")
- yield LintResult.from_cmd(
- flake8("--config=" + config, src.pyarrow, check=False))
-
-
-def python_numpydoc(symbols=None, allow_rules=None, disallow_rules=None):
- """Run numpydoc linter on python.
-
- Pyarrow must be available for import.
- """
- logger.info("Running Python docstring linters")
- # by default try to run on all pyarrow package
- symbols = symbols or {
- 'pyarrow',
- 'pyarrow.compute',
- 'pyarrow.csv',
- 'pyarrow.dataset',
- 'pyarrow.feather',
- 'pyarrow.flight',
- 'pyarrow.fs',
- 'pyarrow.gandiva',
- 'pyarrow.ipc',
- 'pyarrow.json',
- 'pyarrow.orc',
- 'pyarrow.parquet',
- 'pyarrow.plasma',
- 'pyarrow.types',
- }
- try:
- numpydoc = NumpyDoc(symbols)
- except RuntimeError as e:
- logger.error(str(e))
- yield LintResult(success=False)
- return
-
- results = numpydoc.validate(
- # limit the validation scope to the pyarrow package
- from_package='pyarrow',
- allow_rules=allow_rules,
- disallow_rules=disallow_rules
- )
-
- if len(results) == 0:
- yield LintResult(success=True)
- return
-
- number_of_violations = 0
- for obj, result in results:
- errors = result['errors']
-
- # inspect doesn't play nice with cython generated source code,
- # to use a hacky way to represent a proper __qualname__
- doc = getattr(obj, '__doc__', '')
- name = getattr(obj, '__name__', '')
- qualname = getattr(obj, '__qualname__', '')
- module = getattr(obj, '__module__', '')
- instance = getattr(obj, '__self__', '')
- if instance:
- klass = instance.__class__.__name__
- else:
- klass = ''
-
- try:
- cython_signature = doc.splitlines()[0]
- except Exception:
- cython_signature = ''
-
- desc = '.'.join(filter(None, [module, klass, qualname or name]))
-
- click.echo()
- click.echo(click.style(desc, bold=True, fg='yellow'))
- if cython_signature:
- qualname_with_signature = '.'.join([module, cython_signature])
- click.echo(
- click.style(
- '-> {}'.format(qualname_with_signature),
- fg='yellow'
- )
- )
-
- for error in errors:
- number_of_violations += 1
- click.echo('{}: {}'.format(*error))
-
- msg = 'Total number of docstring violations: {}'.format(
- number_of_violations
- )
- click.echo()
- click.echo(click.style(msg, fg='red'))
-
- yield LintResult(success=False)
-
-
-def rat_linter(src, root):
- """Run apache-rat license linter."""
- logger.info("Running apache-rat linter")
-
- exclusion = exclusion_from_globs(
- os.path.join(src.dev, "release", "rat_exclude_files.txt"))
-
- # Creates a git-archive of ArrowSources, apache-rat expects a gzip
- # compressed tar archive.
- archive_path = os.path.join(root, "apache-arrow.tar.gz")
- src.archive(archive_path, compressor=gzip.compress)
- report = Rat().report(archive_path)
-
- violations = list(report.validate(exclusion=exclusion))
- for violation in violations:
- print("apache-rat license violation: {}".format(violation))
-
- yield LintResult(len(violations) == 0)
-
-
-def r_linter(src):
- """Run R linter."""
- logger.info("Running R linter")
- r_lint_sh = os.path.join(src.r, "lint.sh")
- yield LintResult.from_cmd(Bash().run(r_lint_sh, check=False))
-
-
-def rust_linter(src):
- """Run Rust linter."""
- logger.info("Running Rust linter")
- cargo = Cargo()
-
- if not cargo.available:
- logger.error("Rust linter requested but cargo executable not found.")
- return
-
- yield LintResult.from_cmd(cargo.run("+stable", "fmt", "--all", "--",
- "--check", cwd=src.rust,
- check=False))
-
-
-class Hadolint(Command):
- def __init__(self, hadolint_bin=None):
- self.bin = default_bin(hadolint_bin, "hadolint")
-
-
-def is_docker_image(path):
- dirname = os.path.dirname(path)
- filename = os.path.basename(path)
-
- excluded = dirname.startswith(
- "dev") or dirname.startswith("python/manylinux")
-
- return filename.startswith("Dockerfile") and not excluded
-
-
-def docker_linter(src):
- """Run Hadolint docker linter."""
- logger.info("Running Docker linter")
-
- hadolint = Hadolint()
-
- if not hadolint.available:
- logger.error(
- "hadolint linter requested but hadolint binary not found.")
- return
-
- for path in git.ls_files(git_dir=src.path):
- if is_docker_image(path):
- yield LintResult.from_cmd(hadolint.run(path, check=False,
- cwd=src.path))
-
-
-def linter(src, fix=False, *, clang_format=False, cpplint=False,
- clang_tidy=False, iwyu=False, iwyu_all=False,
- python=False, numpydoc=False, cmake_format=False, rat=False,
- r=False, rust=False, docker=False):
- """Run all linters."""
- with tmpdir(prefix="arrow-lint-") as root:
- build_dir = os.path.join(root, "cpp-build")
-
- # Linters yield LintResult without raising exceptions on failure.
- # This allows running all linters in one pass and exposing all
- # errors to the user.
- results = []
-
- if clang_format or cpplint or clang_tidy or iwyu:
- results.extend(cpp_linter(src, build_dir,
- clang_format=clang_format,
- cpplint=cpplint,
- clang_tidy=clang_tidy,
- iwyu=iwyu,
- iwyu_all=iwyu_all,
- fix=fix))
-
- if python:
- results.extend(python_linter(src, fix=fix))
-
- if numpydoc:
- results.extend(python_numpydoc())
-
- if cmake_format:
- results.extend(cmake_linter(src, fix=fix))
-
- if rat:
- results.extend(rat_linter(src, root))
-
- if r:
- results.extend(r_linter(src))
-
- if rust:
- results.extend(rust_linter(src))
-
- if docker:
- results.extend(docker_linter(src))
-
- # Raise error if one linter failed, ensuring calling code can exit with
- # non-zero.
- for result in results:
- result.ok()
diff --git a/dev/archery/archery/utils/logger.py b/dev/archery/archery/utils/logger.py
deleted file mode 100644
index 9d0feda..0000000
--- a/dev/archery/archery/utils/logger.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-import logging
-
-""" Global logger. """
-logger = logging.getLogger("archery")
-
-
-class LoggingContext:
- def __init__(self, quiet=False):
- self.quiet = quiet
-
-
-ctx = LoggingContext()
diff --git a/dev/archery/archery/utils/rat.py b/dev/archery/archery/utils/rat.py
deleted file mode 100644
index e7fe19a..0000000
--- a/dev/archery/archery/utils/rat.py
+++ /dev/null
@@ -1,70 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-import fnmatch
-import re
-from xml.etree import ElementTree
-
-from ..lang.java import Jar
-from .cache import Cache
-from .command import capture_stdout
-
-RAT_VERSION = 0.13
-RAT_JAR_FILENAME = "apache-rat-{}.jar".format(RAT_VERSION)
-RAT_URL_ = "https://repo1.maven.org/maven2/org/apache/rat/apache-rat"
-RAT_URL = "/".join([RAT_URL_, str(RAT_VERSION), RAT_JAR_FILENAME])
-
-
-class Rat(Jar):
- def __init__(self):
- jar = Cache().get_or_insert_from_url(RAT_JAR_FILENAME, RAT_URL)
- Jar.__init__(self, jar)
-
- @capture_stdout(strip=False)
- def run_report(self, archive_path, **kwargs):
- return self.run("--xml", archive_path, **kwargs)
-
- def report(self, archive_path, **kwargs):
- return RatReport(self.run_report(archive_path, **kwargs))
-
-
-def exclusion_from_globs(exclusions_path):
- with open(exclusions_path, 'r') as exclusions_fd:
- exclusions = [e.strip() for e in exclusions_fd]
- return lambda path: any([fnmatch.fnmatch(path, e) for e in exclusions])
-
-
-class RatReport:
- def __init__(self, xml):
- self.xml = xml
- self.tree = ElementTree.fromstring(xml)
-
- def __repr__(self):
- return "RatReport({})".format(self.xml)
-
- def validate(self, exclusion=None):
- for r in self.tree.findall('resource'):
- approvals = r.findall('license-approval')
- if not approvals or approvals[0].attrib['name'] == 'true':
- continue
-
- clean_name = re.sub('^[^/]+/', '', r.attrib['name'])
-
- if exclusion and exclusion(clean_name):
- continue
-
- yield clean_name
diff --git a/dev/archery/archery/utils/report.py b/dev/archery/archery/utils/report.py
deleted file mode 100644
index 6c7587d..0000000
--- a/dev/archery/archery/utils/report.py
+++ /dev/null
@@ -1,64 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-from abc import ABCMeta, abstractmethod
-import datetime
-
-import jinja2
-
-
-def markdown_escape(s):
- for char in ('*', '#', '_', '~', '`', '>'):
- s = s.replace(char, '\\' + char)
- return s
-
-
-class Report(metaclass=ABCMeta):
-
- def __init__(self, **kwargs):
- for field in self.fields:
- if field not in kwargs:
- raise ValueError('Missing keyword argument {}'.format(field))
- self._data = kwargs
-
- def __getattr__(self, key):
- return self._data[key]
-
- @abstractmethod
- def fields(self):
- pass
-
- @property
- @abstractmethod
- def templates(self):
- pass
-
-
-class JinjaReport(Report):
-
- def __init__(self, **kwargs):
- self.env = jinja2.Environment(
- loader=jinja2.PackageLoader('archery', 'templates')
- )
- self.env.filters['md'] = markdown_escape
- self.env.globals['today'] = datetime.date.today
- super().__init__(**kwargs)
-
- def render(self, template_name):
- template_path = self.templates[template_name]
- template = self.env.get_template(template_path)
- return template.render(**self._data)
diff --git a/dev/archery/archery/utils/source.py b/dev/archery/archery/utils/source.py
deleted file mode 100644
index 1ae0fe0..0000000
--- a/dev/archery/archery/utils/source.py
+++ /dev/null
@@ -1,205 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-import os
-from pathlib import Path
-import subprocess
-
-from .git import git
-
-
-class InvalidArrowSource(Exception):
- pass
-
-
-class ArrowSources:
- """ ArrowSources is a companion class representing a directory containing
- Apache Arrow's sources.
- """
- # Note that WORKSPACE is a reserved git revision name by this module to
- # reference the current git workspace. In other words, this indicates to
- # ArrowSources.at_revision that no cloning/checkout is required.
- WORKSPACE = "WORKSPACE"
-
- def __init__(self, path):
- """ Initialize an ArrowSources
-
- The caller must ensure that path is valid arrow source directory (can
- be checked with ArrowSources.valid)
-
- Parameters
- ----------
- path : src
- """
- self.path = Path(path)
-
- @property
- def archery(self):
- """ Returns the archery directory of an Arrow sources. """
- return self.dev / "archery"
-
- @property
- def cpp(self):
- """ Returns the cpp directory of an Arrow sources. """
- return self.path / "cpp"
-
- @property
- def dev(self):
- """ Returns the dev directory of an Arrow sources. """
- return self.path / "dev"
-
- @property
- def python(self):
- """ Returns the python directory of an Arrow sources. """
- return self.path / "python"
-
- @property
- def pyarrow(self):
- """ Returns the python/pyarrow directory of an Arrow sources. """
- return self.python / "pyarrow"
-
- @property
- def r(self):
- """ Returns the r directory of an Arrow sources. """
- return self.path / "r"
-
- @property
- def rust(self):
- """ Returns the rust directory of an Arrow sources. """
- return self.path / "rust"
-
- @property
- def git_backed(self):
- """ Indicate if the sources are backed by git. """
- return (self.path / ".git").exists()
-
- @property
- def git_dirty(self):
- """ Indicate if the sources is a dirty git directory. """
- return self.git_backed and git.dirty(git_dir=self.path)
-
- def archive(self, path, dereference=False, compressor=None, revision=None):
- """ Saves a git archive at path. """
- if not self.git_backed:
- raise ValueError("{} is not backed by git".format(self))
-
- rev = revision if revision else "HEAD"
- archive = git.archive("--prefix=apache-arrow/", rev,
- git_dir=self.path)
-
- # TODO(fsaintjacques): fix dereference for
-
- if compressor:
- archive = compressor(archive)
-
- with open(path, "wb") as archive_fd:
- archive_fd.write(archive)
-
- def at_revision(self, revision, clone_dir):
- """ Return a copy of the current sources for a specified git revision.
-
- This method may return the current object if no checkout is required.
- The caller is responsible to remove the cloned repository directory.
-
- The user can use the special WORKSPACE token to mean the current git
- workspace (no checkout performed).
-
- The second value of the returned tuple indicates if a clone was
- performed.
-
- Parameters
- ----------
- revision : str
- Revision to checkout sources at.
- clone_dir : str
- Path to checkout the local clone.
- """
- if not self.git_backed:
- raise ValueError("{} is not backed by git".format(self))
-
- if revision == ArrowSources.WORKSPACE:
- return self, False
-
- # A local clone is required to leave the current sources intact such
- # that builds depending on said sources are not invalidated (or worse
- # slightly affected when re-invoking the generator).
- # "--local" only works when dest dir is on same volume of source dir.
- # "--shared" works even if dest dir is on different volume.
- git.clone("--shared", self.path, clone_dir)
-
- # Revision can reference "origin/" (or any remotes) that are not found
- # in the local clone. Thus, revisions are dereferenced in the source
- # repository.
- original_revision = git.rev_parse(revision)
-
- git.checkout(original_revision, git_dir=clone_dir)
-
- return ArrowSources(clone_dir), True
-
- @staticmethod
- def find(path=None):
- """ Infer Arrow sources directory from various method.
-
- The following guesses are done in order until a valid match is found:
-
- 1. Checks the given optional parameter.
-
- 2. Checks if the environment variable `ARROW_SRC` is defined and use
- this.
-
- 3. Checks if the current working directory (cwd) is an Arrow source
- directory.
-
- 4. Checks if this file (cli.py) is still in the original source
- repository. If so, returns the relative path to the source
- directory.
- """
-
- # Explicit via environment
- env = os.environ.get("ARROW_SRC")
-
- # Implicit via cwd
- cwd = Path.cwd()
-
- # Implicit via current file
- try:
- this = Path(__file__).parents[4]
- except IndexError:
- this = None
-
- # Implicit via git repository (if archery is installed system wide)
- try:
- repo = git.repository_root(git_dir=cwd)
- except subprocess.CalledProcessError:
- # We're not inside a git repository.
- repo = None
-
- paths = list(filter(None, [path, env, cwd, this, repo]))
- for p in paths:
- try:
- return ArrowSources(p)
- except InvalidArrowSource:
- pass
-
- searched_paths = "\n".join([" - {}".format(p) for p in paths])
- raise InvalidArrowSource(
- "Unable to locate Arrow's source directory. "
- "Searched paths are:\n{}".format(searched_paths)
- )
-
- def __repr__(self):
- return self.path
diff --git a/dev/archery/archery/utils/tmpdir.py b/dev/archery/archery/utils/tmpdir.py
deleted file mode 100644
index 07d7355..0000000
--- a/dev/archery/archery/utils/tmpdir.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-from contextlib import contextmanager
-from tempfile import mkdtemp, TemporaryDirectory
-
-
-@contextmanager
-def tmpdir(preserve=False, prefix="arrow-archery-"):
- if preserve:
- yield mkdtemp(prefix=prefix)
- else:
- with TemporaryDirectory(prefix=prefix) as tmp:
- yield tmp
diff --git a/dev/archery/conftest.py b/dev/archery/conftest.py
deleted file mode 100644
index 06a643b..0000000
--- a/dev/archery/conftest.py
+++ /dev/null
@@ -1,70 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-import pathlib
-
-import pytest
-
-
-def pytest_addoption(parser):
- parser.addoption(
- "--enable-integration",
- action="store_true",
- default=False,
- help="run slow tests"
- )
-
-
-def pytest_configure(config):
- config.addinivalue_line(
- "markers",
- (
- "integration: mark test as integration tests involving more "
- "extensive setup (only used for crossbow at the moment)"
- )
- )
-
-
-def pytest_collection_modifyitems(config, items):
- if config.getoption("--enable-integration"):
- return
- marker = pytest.mark.skip(reason="need --enable-integration option to run")
- for item in items:
- if "integration" in item.keywords:
- item.add_marker(marker)
-
-
-@pytest.fixture
-def load_fixture(request):
- current_test_directory = pathlib.Path(request.node.fspath).parent
-
- def decoder(path):
- with path.open('r') as fp:
- if path.suffix == '.json':
- import json
- return json.load(fp)
- elif path.suffix == '.yaml':
- import yaml
- return yaml.load(fp)
- else:
- return fp.read()
-
- def loader(name, decoder=decoder):
- path = current_test_directory / 'fixtures' / name
- return decoder(path)
-
- return loader
diff --git a/dev/archery/generate_files_for_endian_test.sh b/dev/archery/generate_files_for_endian_test.sh
deleted file mode 100755
index 54019ea..0000000
--- a/dev/archery/generate_files_for_endian_test.sh
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/bin/bash
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-# This script generates json and arrow files of each type (e.g. primitive) for integration endian test
-# Usage: generate_files_for_endian_test.sh
-# ARROW_CPP_EXE_PATH : where Arrow C++ binaries can be found
-# TMP_DIR : where files will be generated
-
-set -e
-
-: ${ARROW_CPP_EXE_PATH:=/arrow/cpp/build/debug/}
-: ${TMP_DIR:=/tmp/arrow}
-
-json_dir=$TMP_DIR/arrow.$$
-mkdir -p $json_dir
-
-archery integration --stop-on-error --with-cpp=1 --tempdir=$json_dir
-
-for f in $json_dir/*.json ; do
- $ARROW_CPP_EXE_PATH/arrow-json-integration-test -mode JSON_TO_ARROW -json $f -arrow ${f%.*}.arrow_file -integration true ;
-done
-for f in $json_dir/*.arrow_file ; do
- $ARROW_CPP_EXE_PATH/arrow-file-to-stream $f > ${f%.*}.stream;
-done
-for f in $json_dir/*.json ; do
- gzip $f ;
-done
-echo "The files are under $json_dir"
diff --git a/dev/archery/requirements-lint.txt b/dev/archery/requirements-lint.txt
deleted file mode 100644
index fc7f339..0000000
--- a/dev/archery/requirements-lint.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-autopep8
-flake8
-cmake_format==0.5.2
diff --git a/dev/archery/requirements.txt b/dev/archery/requirements.txt
deleted file mode 100644
index 0e1258a..0000000
--- a/dev/archery/requirements.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-click
-pygithub
-python-dotenv
-ruamel.yaml
diff --git a/dev/archery/setup.py b/dev/archery/setup.py
deleted file mode 100755
index 0537e8b..0000000
--- a/dev/archery/setup.py
+++ /dev/null
@@ -1,62 +0,0 @@
-#!/usr/bin/env python
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-import functools
-import operator
-import sys
-from setuptools import setup
-
-if sys.version_info < (3, 6):
- sys.exit('Python < 3.6 is not supported')
-
-# For pathlib.Path compatibility
-jinja_req = 'jinja2>=2.11'
-
-extras = {
- 'benchmark': ['pandas'],
- 'docker': ['ruamel.yaml', 'python-dotenv'],
- 'release': [jinja_req, 'jira', 'semver', 'gitpython'],
- 'crossbow': ['github3.py', jinja_req, 'pygit2', 'ruamel.yaml',
- 'setuptools_scm'],
-}
-extras['bot'] = extras['crossbow'] + ['pygithub', 'jira']
-extras['all'] = list(set(functools.reduce(operator.add, extras.values())))
-
-setup(
- name='archery',
- version="0.1.0",
- description='Apache Arrow Developers Tools',
- url='http://github.com/apache/arrow',
- maintainer='Arrow Developers',
- maintainer_email='dev@arrow.apache.org',
- packages=[
- 'archery',
- 'archery.benchmark',
- 'archery.integration',
- 'archery.lang',
- 'archery.utils'
- ],
- include_package_data=True,
- install_requires=['click>=7'],
- tests_require=['pytest', 'responses'],
- extras_require=extras,
- entry_points='''
- [console_scripts]
- archery=archery.cli:archery
- '''
-)
diff --git a/dev/release/rat_exclude_files.txt b/dev/release/rat_exclude_files.txt
index f3eb273..d2b5aad 100644
--- a/dev/release/rat_exclude_files.txt
+++ b/dev/release/rat_exclude_files.txt
@@ -1,94 +1,14 @@
*.npmrc
*.gitignore
.gitmodules
-*_generated.h
-*_generated.js
-*_generated.ts
*.csv
*.json
*.snap
.github/ISSUE_TEMPLATE/*.md
.github/pull_request_template.md
-ci/etc/rprofile
-ci/etc/*.patch
-ci/vcpkg/*.patch
CHANGELOG.md
-dev/requirements*.txt
-dev/archery/MANIFEST.in
-dev/archery/requirements*.txt
-dev/archery/archery/tests/fixtures/*
-dev/archery/archery/crossbow/tests/fixtures/*
-dev/release/rat_exclude_files.txt
-dev/tasks/homebrew-formulae/apache-arrow.rb
-dev/tasks/linux-packages/apache-arrow-apt-source/debian/apache-arrow-apt-source.install
-dev/tasks/linux-packages/apache-arrow-apt-source/debian/compat
-dev/tasks/linux-packages/apache-arrow-apt-source/debian/control
-dev/tasks/linux-packages/apache-arrow-apt-source/debian/rules
-dev/tasks/linux-packages/apache-arrow-apt-source/debian/source/format
-dev/tasks/linux-packages/apache-arrow/debian/compat
-dev/tasks/linux-packages/apache-arrow/debian/control.in
-dev/tasks/linux-packages/apache-arrow/debian/gir1.2-arrow-1.0.install
-dev/tasks/linux-packages/apache-arrow/debian/gir1.2-arrow-cuda-1.0.install
-dev/tasks/linux-packages/apache-arrow/debian/gir1.2-arrow-dataset-1.0.install
-dev/tasks/linux-packages/apache-arrow/debian/gir1.2-gandiva-1.0.install
-dev/tasks/linux-packages/apache-arrow/debian/gir1.2-parquet-1.0.install
-dev/tasks/linux-packages/apache-arrow/debian/gir1.2-plasma-1.0.install
-dev/tasks/linux-packages/apache-arrow/debian/libarrow-dev.install
-dev/tasks/linux-packages/apache-arrow/debian/libarrow-glib-dev.install
-dev/tasks/linux-packages/apache-arrow/debian/libarrow-glib-doc.doc-base
-dev/tasks/linux-packages/apache-arrow/debian/libarrow-glib-doc.install
-dev/tasks/linux-packages/apache-arrow/debian/libarrow-glib-doc.links
-dev/tasks/linux-packages/apache-arrow/debian/libarrow-glib400.install
-dev/tasks/linux-packages/apache-arrow/debian/libarrow-cuda-dev.install
-dev/tasks/linux-packages/apache-arrow/debian/libarrow-cuda-glib-dev.install
-dev/tasks/linux-packages/apache-arrow/debian/libarrow-cuda-glib400.install
-dev/tasks/linux-packages/apache-arrow/debian/libarrow-cuda400.install
-dev/tasks/linux-packages/apache-arrow/debian/libarrow-dataset-dev.install
-dev/tasks/linux-packages/apache-arrow/debian/libarrow-dataset-glib-dev.install
-dev/tasks/linux-packages/apache-arrow/debian/libarrow-dataset-glib-doc.doc-base
-dev/tasks/linux-packages/apache-arrow/debian/libarrow-dataset-glib-doc.install
-dev/tasks/linux-packages/apache-arrow/debian/libarrow-dataset-glib-doc.links
-dev/tasks/linux-packages/apache-arrow/debian/libarrow-dataset-glib400.install
-dev/tasks/linux-packages/apache-arrow/debian/libarrow-dataset400.install
-dev/tasks/linux-packages/apache-arrow/debian/libarrow-flight-dev.install
-dev/tasks/linux-packages/apache-arrow/debian/libarrow-flight400.install
-dev/tasks/linux-packages/apache-arrow/debian/libarrow-python-dev.install
-dev/tasks/linux-packages/apache-arrow/debian/libarrow-python-flight-dev.install
-dev/tasks/linux-packages/apache-arrow/debian/libarrow-python-flight400.install
-dev/tasks/linux-packages/apache-arrow/debian/libarrow-python400.install
-dev/tasks/linux-packages/apache-arrow/debian/libarrow400.install
-dev/tasks/linux-packages/apache-arrow/debian/libgandiva-dev.install
-dev/tasks/linux-packages/apache-arrow/debian/libgandiva-glib-dev.install
-dev/tasks/linux-packages/apache-arrow/debian/libgandiva-glib-doc.doc-base
-dev/tasks/linux-packages/apache-arrow/debian/libgandiva-glib-doc.install
-dev/tasks/linux-packages/apache-arrow/debian/libgandiva-glib-doc.links
-dev/tasks/linux-packages/apache-arrow/debian/libgandiva-glib400.install
-dev/tasks/linux-packages/apache-arrow/debian/libgandiva400.install
-dev/tasks/linux-packages/apache-arrow/debian/libparquet-dev.install
-dev/tasks/linux-packages/apache-arrow/debian/libparquet-glib-dev.install
-dev/tasks/linux-packages/apache-arrow/debian/libparquet-glib-doc.doc-base
-dev/tasks/linux-packages/apache-arrow/debian/libparquet-glib-doc.install
-dev/tasks/linux-packages/apache-arrow/debian/libparquet-glib-doc.links
-dev/tasks/linux-packages/apache-arrow/debian/libparquet-glib400.install
-dev/tasks/linux-packages/apache-arrow/debian/libparquet400.install
-dev/tasks/linux-packages/apache-arrow/debian/libplasma-dev.install
-dev/tasks/linux-packages/apache-arrow/debian/libplasma-glib-dev.install
-dev/tasks/linux-packages/apache-arrow/debian/libplasma-glib-doc.doc-base
-dev/tasks/linux-packages/apache-arrow/debian/libplasma-glib-doc.install
-dev/tasks/linux-packages/apache-arrow/debian/libplasma-glib-doc.links
-dev/tasks/linux-packages/apache-arrow/debian/libplasma-glib400.install
-dev/tasks/linux-packages/apache-arrow/debian/libplasma400.install
-dev/tasks/linux-packages/apache-arrow/debian/patches/series
-dev/tasks/linux-packages/apache-arrow/debian/plasma-store-server.install
-dev/tasks/linux-packages/apache-arrow/debian/rules
-dev/tasks/linux-packages/apache-arrow/debian/source/format
-dev/tasks/linux-packages/apache-arrow/debian/watch
-dev/tasks/requirements*.txt
-dev/tasks/conda-recipes/*
pax_global_header
MANIFEST.in
-__init__.pxd
-__init__.py
requirements.txt
*.html
*.sgml