Git Product home page Git Product logo

banditore's Introduction

Banditore

CI Coveralls Status PHPStan level max

Banditore retrieves new releases from your GitHub starred repositories and put them in a RSS feed, just for you.

Requirements

  • PHP >= 7.4 (with pdo_mysql)
  • MySQL >= 5.7
  • Redis (to cache requests to the GitHub API)
  • RabbitMQ, which is optional (see below)
  • Supervisor (only if you use RabbitMQ)
  • NVM & Yarn to install assets

Installation

  1. Clone the project

    git clone https://github.com/j0k3r/banditore.git
  2. Register a new OAuth GitHub application and get the Client ID & Client Secret for the next step (for the Authorization callback URL put http://127.0.0.1:8000/callback)

  3. Install dependencies using Composer and define your parameter during the installation

    APP_ENV=prod composer install -o --no-dev

    If you want to use:

    • Sentry to retrieve all errors, register here and get your dsn (in Project Settings > DSN).
  4. Setup the database

    php bin/console doctrine:database:create -e prod
    php bin/console doctrine:schema:create -e prod
  5. Install assets

    nvm install
    yarn install
  6. You can now launch the website:

    php bin/console server:run -e prod

    And access it at this address: http://127.0.0.1:8000

Running the instance

Once the website is up, you now have to setup few things to retrieve new releases. You have two choices:

  • using crontab command (very simple and ok if you are alone)
  • using RabbitMQ (might be better if you plan to have more than few persons but it's more complex) ๐Ÿค™

Without RabbitMQ

You just need to define these 2 cronjobs (replace all /path/to/banditore with real value):

# retrieve new release of each repo every 10 minutes
*/10  *   *   *   *   php /path/to/banditore/bin/console -e prod banditore:sync:versions >> /path/to/banditore/var/logs/command-sync-versions.log 2>&1
# sync starred repos of each user every 5 minutes
*/5   *   *   *   *   php /path/to/banditore/bin/console -e prod banditore:sync:starred-repos >> /path/banditore/to/var/logs/command-sync-repos.log 2>&1

With RabbitMQ

  1. You'll need to declare exchanges and queues. Replace guest by the user of your RabbitMQ instance (guest is the default one):
php bin/console messenger:setup-transports -vvv sync_starred_repos
php bin/console messenger:setup-transports -vvv sync_versions
  1. You now have two queues and two exchanges defined:
  • banditore.sync_starred_repos: will receive messages to sync starred repos of all users
  • banditore.sync_versions: will receive message to retrieve new release for repos
  1. Enable these 2 cronjobs which will periodically push messages in queues (replace all /path/to/banditore with real value):
# retrieve new release of each repo every 10 minutes
*/10  *   *   *   *   php /path/to/banditore/bin/console -e prod banditore:sync:versions --use_queue >> /path/to/banditore/var/logs/command-sync-versions.log 2>&1
# sync starred repos of each user every 5 minutes
*/5   *   *   *   *   php /path/to/banditore/bin/console -e prod banditore:sync:starred-repos --use_queue >> /path/banditore/to/var/logs/command-sync-repos.log 2>&1
  1. Setup Supervisor using the sample file from the repo. You can copy/paste it into /etc/supervisor/conf.d/ and adjust path. The default file will launch:
  • 2 workers for sync starred repos
  • 4 workers to fetch new releases

Once you've put the file in the supervisor conf repo, run supervisorctl update && supervisorctl start all (update will read your conf, start all will start all workers)

Monitoring

There is a status page available at /status, it returns a json with some information about the freshness of fetched versions:

{
    "latest": {
        "date": "2019-09-17 19:50:50.000000",
        "timezone_type": 3,
        "timezone": "Europe\/Berlin"
    },
    "diff": 1736,
    "is_fresh": true
}
  • latest: the latest created version as a DateTime
  • diff: the difference between now and the latest created version (in seconds)
  • is_fresh: indicate if everything is fine by comparing the diff above with the status_minute_interval_before_alert parameter

For example, I've setup a check on updown.io to check that status page and if the page contains "is_fresh":true. So I receive an alert when is_fresh is false: which means there is a potential issue on the server.

Running the test suite

If you plan to contribute (you're awesome, I know that โœŒ๏ธ), you'll need to install the project in a different way (for example, to retrieve dev packages):

git clone https://github.com/j0k3r/banditore.git
composer install -o
php bin/console doctrine:database:create -e=test
php bin/console doctrine:schema:create -e=test
php bin/console doctrine:fixtures:load --env=test -n
php bin/simple-phpunit -v

By default the test connexion login is root without password. You can change it in app/config/config_test.yml.

How it works

Ok, if you goes that deeper in the readme, it means you're a bit more than interested, I like that.

Retrieving new release / tag

This is the complex part of the app. Here is a simplified solution to achieve it.

New release

It's not as easy as using the /repos/:owner/:repo/releases API endpoint to retrieve latest release for a given repo. Because not all repo owner use that feature (which is a shame in my case).

All information for a release are available on that endpoint:

  • name of the tag (ie: v1.0.0)
  • name of the release (ie: yay first release)
  • published date
  • description of the release

Check a new release of that repo as example: https://api.github.com/repos/j0k3r/banditore/releases/5770680

New tag

Some owners also use tag which is a bit more complex to retrieve all information because a tag only contains information about the SHA-1 of the commit which was used to make the tag. We only have these information:

  • name of the tag (ie: v1.4.2)
  • name of the release will be the name of the tag, in that case

Check tag list of swarrot/SwarrotBundle as example: https://api.github.com/repos/swarrot/SwarrotBundle/tags

After retrieving the tag, we need to retrieve the commit to get these information:

  • date of the commit
  • message of the commit

Check a commit from the previous tag list as example: https://api.github.com/repos/swarrot/SwarrotBundle/commits/84c7c57622e4666ae5706f33cd71842639b78755

GitHub Client Discovery

This is the most important piece of the app. One thing that I ran though is hitting the rate limit on GitHub. The rate limit for a given authenticated client is 5.000 calls per hour. This limit is never reached when looking for new release (thanks to the conditional requests of the GitHub API) on a daily basis.

But when new user sign in, we need to sync all its starred repositories and also all their releases / tags. And here come the gourmand part:

  • one call for the list of release
  • one call to retrieve information of each tag (if the repo doesn't have release)
  • one call for each release to convert markdown text to html

Let's say the repo:

  • has 50 tags: 1 (get tag list) + 50 (get commit information) + 50 (convert markdown) = 101 calls.
  • has 50 releases: 1 (get tag list) + 50 (get each release) + 50 (convert markdown) = 101 calls.

And keep in mind that some repos got also 1.000+ tags (!!).

To avoid hitting the limit in such case and wait 1 hour to be able to make requests again I created the GitHub Client Discovery class. It aims to find the best client with enough rate limit remain (defined as 50).

  • it first checks using the GitHub OAuth app
  • then it checks using all user GitHub token

Which means, if you have 5 users on the app, you'll be able to make (1 + 5) x 5.000 = 30.000 calls per hour

banditore's People

Contributors

dependabot-preview[bot] avatar dependabot[bot] avatar github-actions[bot] avatar j0k3r avatar thenanobel avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

banditore's Issues

Avoid too much exception when no client is available

When reaching limit of all our clients, we shouldn't send an exception but just skip the message.

[2017-03-06 13:59:40] app.ERROR: [Ack] An exception occurred. Message #19 has been nack'ed. {"swarrot_processor":"ack","exception":"[object] (RuntimeException(code: 0): No way to authenticate a client with enough rate limit remaining :( at /www/src/AppBundle/Github/ClientDiscovery.php:95)"} 
[2017-03-06 13:59:40] app.ERROR: [ExceptionCatcher] An exception occurred. This exception has been caught. {"swarrot_processor":"exception","exception":"[object] (RuntimeException(code: 0): No way to authenticate a client with enough rate limit remaining :( at /www/src/AppBundle/Github/ClientDiscovery.php:95)"}

Sending an exception for each message killed Sentry free tier limit

Add a global stats page?

Which can be turned off in parameters.
But these stats could be interesting:

  • nb of repos
  • nb of releases
  • average release per repo (which might be a result from the 2 previous numbers)
  • top 5 repos with most tags/releases
  • average number of stars per user

"RateLimit reached, stopping"

Hi,
first: thank you for your project. I have been using bandito.re for the last year and it was working very nicely.

I have noticed that there hasn't been an update on my RSS feed and judging by your website, this should be true for everyone else. ("Last update: 5 days ago")
So today I have installed Banditore on my own server to see if it runs for me.

When running banditore:sync:versions I got some errors after ~100 checks in my logfile

[โ€ฆ]
15:31:06 WARNING   [app] RateLimit reached, stopping.
[728/731] Check 187335810 โ€ฆ
15:31:06 WARNING   [app] RateLimit reached, stopping.
[729/731] Check 191083066 โ€ฆ
15:31:06 WARNING   [app] RateLimit reached, stopping.
[730/731] Check 192097288 โ€ฆ
15:31:07 WARNING   [app] RateLimit reached, stopping.
[731/731] Check 194528327 โ€ฆ
15:31:07 WARNING   [app] RateLimit reached, stopping.
Repo checked: 731

Did GitHub changed something on their API or what seems to be the problem?

Better notification about sync for new user

Currently, we sync only starred repos when user first logged-in (well, at each login in fact).
We should display a message about sync in progress after the first login to avoid user with only few repos on its dashboard (repo which were already synced from other users).

Also, display a message that version are sync every 20 minutes (for now)

Handle blocked repository

18:50:56 WARNING   [app] (repo/tags) Repository access blocked
18:50:56 NOTICE    [app] [2794]  new versions for Bukkit/CraftBukkit

https://github.com/Bukkit/CraftBukkit is unavailable.
Instead of retrying every time to retrieve new version, we should flag it as removed, see:

try {
$tags = $this->client->api('repo')->tags($username, $repoName, ['per_page' => 1, 'page' => 1]);
} catch (\Exception $e) {
$this->logger->warning('(repo/tags) <error>' . $e->getMessage() . '</error>');
// repo not found? Ignore it in future loops
if (404 === $e->getCode()) {
$repo->setRemovedAt(new \DateTime());
$em->persist($repo);
}
return;
}

The EntityManager is closed

Following #16, we should really use doctrine_ping.

[2017-03-06 15:04:23] app.ERROR: Error while syncing repo {"exception":"Doctrine\\DBAL\\Exception\\UniqueConstraintViolationException","message":"An exception occurred while executing 'INSERT INTO version (tag_name, name, prerelease, created_at, body, repo_id) VALUES (?, ?, ?, ?, ?, ?)' with params [\"Vanilla_2.0.18rc3\", \"Vanilla_2.0.18rc3\", 0, \"2011-10-30 02:01:50\", \"<p>Fixed bug with restoring discussions from the spam log.<\\/p>\", 232390]:\n\nSQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '232390-Vanilla_2.0.18rc3' for key 'repo_version_unique'","repo":"vanilla/vanilla"} 
[2017-03-06 15:04:24] app.NOTICE: Consume banditore.sync_versions message {"repo":"henrikbjorn/GravatarBundle"} 
[2017-03-06 15:04:24] app.NOTICE: [3977] Check henrikbjorn/GravatarBundle โ€ฆ   
[2017-03-06 15:04:24] app.ERROR: Error while syncing repo {"exception":"Doctrine\\ORM\\ORMException","message":"The EntityManager is closed.","repo":"henrikbjorn/GravatarBundle"}

Implement Retry after middleware for markdown endpoint

The markdown endpoint is subject to the abuse rate limit.
Which means after fetching new releases for new repository with a lot of releases, the markdown endpoint is called for each release (in CheckNewVersionCommand) and can generate abuse rate limit. The release won't be saved.

Instead of jumping to next tag, we should implement the Retry-After header returned from Github for that particular case.

Here is the exception thrown:

object(Github\Exception\RuntimeException)#25031 (7) {
  ["message":protected]=>
  string(96) "You have triggered an abuse detection mechanism. Please wait a few minutes before you try again."
  ["string":"Exception":private]=>
  string(0) ""
  ["code":protected]=>
  int(403)
  ["file":protected]=>
  string(106) "/home/www/bandito.re/www/vendor/knplabs/github-api/lib/Github/HttpClient/Plugin/GithubExceptionThrower.php"
  ["line":protected]=>
  int(87)
  ["trace":"Exception":private]=>
  array(19) {
    [0]=>
    array(6) {
      ["file"]=>
      string(85) "/home/www/bandito.re/www/vendor/php-http/httplug/src/Promise/HttpFulfilledPromise.php"
      ["line"]=>
      int(34)
      ["function"]=>
      string(34) "Github\HttpClient\Plugin\{closure}"
      ["class"]=>
      string(47) "Github\HttpClient\Plugin\GithubExceptionThrower"
      ["type"]=>
      string(2) "->"
      ["args"]=>
      array(1) {
        [0]=>
        object(GuzzleHttp\Psr7\Response)#25058 (6) {
          ["reasonPhrase":"GuzzleHttp\Psr7\Response":private]=>
          string(9) "Forbidden"
          ["statusCode":"GuzzleHttp\Psr7\Response":private]=>
          int(403)
          ["headers":"GuzzleHttp\Psr7\Response":private]=>
          array(16) {
            ["Server"]=>
            array(1) {
              [0]=>
              string(10) "GitHub.com"
            }
            ["Date"]=>
            array(1) {
              [0]=>
              string(29) "Sun, 19 Feb 2017 12:44:42 GMT"
            }
            ["Content-Type"]=>
            array(1) {
              [0]=>
              string(31) "application/json; charset=utf-8"
            }
            ["Transfer-Encoding"]=>
            array(1) {
              [0]=>
              string(7) "chunked"
            }
            ["Status"]=>
            array(1) {
              [0]=>
              string(13) "403 Forbidden"
            }
            ["Retry-After"]=>
            array(1) {
              [0]=>
              string(2) "60"

The retry middleware from Guzzle 6 can handle that case.

We should add a custom class to build the Guzzle Client and inject this middleware.

Here is a start about services to inject custom Guzzle client into the Github Client API.

services:
    banditore.client.guzzle:
        class: GuzzleHttp\Client

    # guzzle adaptater to inject custom client into Github client API
    banditore.guzzle.adapter:
        class: Http\Adapter\Guzzle6\Client
        arguments:
            - "@banditore.client.guzzle"

    # github http builder to inject custom Guzzle client
    banditore.client.github.http_builder:
        class: Github\HttpClient\Builder
        arguments:
            - "@banditore.guzzle.adapter"

    # global Github application client
    banditore.client.github.application:
        class: Github\Client
        arguments:
            - "@banditore.client.github.http_builder"
        calls:
            - [ authenticate, [ "%github_client_id%", "%github_client_secret%", !php/const:Github\Client::AUTH_URL_CLIENT_ID ] ]

Unable to log in

Visiting the https://bandito.re/connect URL is giving me the following error :

League\OAuth2\Client\Provider\Exception\GithubIdentityProviderException:
Not Found
Related stack trace
League\OAuth2\Client\Provider\Exception\GithubIdentityProviderException:
Not Found

  at vendor/league/oauth2-github/src/Provider/Exception/GithubIdentityProviderException.php:51
  at League\OAuth2\Client\Provider\Exception\GithubIdentityProviderException::fromResponse()
     (vendor/league/oauth2-github/src/Provider/Exception/GithubIdentityProviderException.php:21)
  at League\OAuth2\Client\Provider\Exception\GithubIdentityProviderException::clientException()
     (vendor/league/oauth2-github/src/Provider/Github.php:110)
  at League\OAuth2\Client\Provider\Github->checkResponse()
     (vendor/league/oauth2-client/src/Provider/AbstractProvider.php:628)
  at League\OAuth2\Client\Provider\AbstractProvider->getParsedResponse()
     (vendor/league/oauth2-github/src/Provider/Github.php:74)
  at League\OAuth2\Client\Provider\Github->fetchResourceOwnerDetails()
     (vendor/league/oauth2-client/src/Provider/AbstractProvider.php:767)
  at League\OAuth2\Client\Provider\AbstractProvider->getResourceOwner()
     (vendor/knpuniversity/oauth2-client-bundle/src/Client/OAuth2Client.php:139)
  at KnpU\OAuth2ClientBundle\Client\OAuth2Client->fetchUserFromToken()
     (vendor/knpuniversity/oauth2-client-bundle/src/Client/Provider/GithubClient.php:24)
  at KnpU\OAuth2ClientBundle\Client\Provider\GithubClient->fetchUserFromToken()
     (src/Security/GithubAuthenticator.php:51)
  at App\Security\GithubAuthenticator->App\Security\{closure}()
     (vendor/symfony/security-http/Authenticator/Passport/Badge/UserBadge.php:71)
  at Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge->getUser()
     (vendor/symfony/security-http/Authenticator/Passport/Passport.php:59)
  at Symfony\Component\Security\Http\Authenticator\Passport\Passport->getUser()
     (vendor/symfony/security-http/EventListener/UserCheckerListener.php:43)
  at Symfony\Component\Security\Http\EventListener\UserCheckerListener->preCheckCredentials()
     (vendor/symfony/event-dispatcher/EventDispatcher.php:270)
  at Symfony\Component\EventDispatcher\EventDispatcher::Symfony\Component\EventDispatcher\{closure}()
     (vendor/symfony/event-dispatcher/EventDispatcher.php:230)
  at Symfony\Component\EventDispatcher\EventDispatcher->callListeners()
     (vendor/symfony/event-dispatcher/EventDispatcher.php:59)
  at Symfony\Component\EventDispatcher\EventDispatcher->dispatch()
     (vendor/symfony/security-http/Authentication/AuthenticatorManager.php:184)
  at Symfony\Component\Security\Http\Authentication\AuthenticatorManager->executeAuthenticator()
     (vendor/symfony/security-http/Authentication/AuthenticatorManager.php:161)
  at Symfony\Component\Security\Http\Authentication\AuthenticatorManager->executeAuthenticators()
     (vendor/symfony/security-http/Authentication/AuthenticatorManager.php:141)
  at Symfony\Component\Security\Http\Authentication\AuthenticatorManager->authenticateRequest()
     (vendor/symfony/security-http/Firewall/AuthenticatorManagerListener.php:40)
  at Symfony\Component\Security\Http\Firewall\AuthenticatorManagerListener->authenticate()
     (vendor/symfony/security-http/Authenticator/Debug/TraceableAuthenticatorManagerListener.php:65)
  at Symfony\Component\Security\Http\Authenticator\Debug\TraceableAuthenticatorManagerListener->authenticate()
     (vendor/symfony/security-bundle/Debug/WrappedLazyListener.php:49)
  at Symfony\Bundle\SecurityBundle\Debug\WrappedLazyListener->authenticate()
     (vendor/symfony/security-bundle/Security/LazyFirewallContext.php:60)
  at Symfony\Bundle\SecurityBundle\Security\LazyFirewallContext->__invoke()
     (vendor/symfony/security-bundle/Debug/TraceableFirewallListener.php:70)
  at Symfony\Bundle\SecurityBundle\Debug\TraceableFirewallListener->callListeners()
     (vendor/symfony/security-http/Firewall.php:92)
  at Symfony\Component\Security\Http\Firewall->onKernelRequest()
     (vendor/symfony/event-dispatcher/Debug/WrappedListener.php:117)
  at Symfony\Component\EventDispatcher\Debug\WrappedListener->__invoke()
     (vendor/symfony/event-dispatcher/EventDispatcher.php:230)
  at Symfony\Component\EventDispatcher\EventDispatcher->callListeners()
     (vendor/symfony/event-dispatcher/EventDispatcher.php:59)
  at Symfony\Component\EventDispatcher\EventDispatcher->dispatch()
     (vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php:154)
  at Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher->dispatch()
     (vendor/symfony/http-kernel/HttpKernel.php:139)
  at Symfony\Component\HttpKernel\HttpKernel->handleRaw()
     (vendor/symfony/http-kernel/HttpKernel.php:75)
  at Symfony\Component\HttpKernel\HttpKernel->handle()
     (vendor/symfony/http-kernel/Kernel.php:202)
  at Symfony\Component\HttpKernel\Kernel->handle()
     (vendor/symfony/runtime/Runner/Symfony/HttpKernelRunner.php:35)
  at Symfony\Component\Runtime\Runner\Symfony\HttpKernelRunner->run()
     (vendor/autoload_runtime.php:35)
  at require_once('/home/ubuntu/banditore/vendor/autoload_runtime.php')
     (public/index.php:5)    

Let me know if you need anything more to help diagnose the issue !

Also, I would happily submit a PR if you give me some guidance about the fix.

Have a nice day !

How to install Banditore?

Hello @j0k3r, with the launch of the first beta, do you plans to write a short doc on how to install the app? It could be useful for people like me who want to test your project and send you some feedback.

Thanks a lot!

Worker lost connection to MySQL

Sometimes worker lost connect to Doctrine and generate this kind of error:

[2017-02-24 09:12:06] app.NOTICE: Consume banditore.sync_user_repo message {"user":"j0k3r"}
[2017-02-24 09:12:07] app.INFO:     sync 83 starred repos {"user":"j0k3r","rate":4935}
[2017-02-24 09:12:07] php.WARNING: Warning: Error while sending QUERY packet. PID=24148 {"exception":"[object] (ErrorException(code: 0): Warning: Error while sending QUERY packet. PID=24148 at /www/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php:91)"}
[2017-02-24 09:12:07] app.ERROR: Error while sending data to user {"exception":"An exception occurred while executing 'SELECT t0.id AS id_1, t0.created_at AS created_at_2, t0.user_id AS user_id_3, t0.repo_id AS repo_id_4 FROM star t0 WHERE t0.repo_id = ? AND t0.user_id = ? LIMIT 1' with params [82027290, 121870]:\n\nSQLSTATE[HY000]: General error: 2006 MySQL server has gone away","user":"j0k3r"}

We should test the doctrine_ping processor of Swarrot.

Or handle it manually: http://stackoverflow.com/q/14060507/569101

Add a header to each RSS item with repo information

Something like:

<table>
   <tr>
      <td>
         <a href="https://github.com/wallabag/wallabag">
            <img src="https://avatars.githubusercontent.com/u/4143872?v=3&amp;s=80" alt="wallabag/wallabag" title="wallabag/wallabag" />
         </a>
      </td>
      <td>
         <p>
            <b>
               <a href="https://github.com/wallabag/wallabag">wallabag/wallabag</a>
            </b>
            (
            <a href="https://www.wallabag.org/">https://www.wallabag.org/</a>
            )
         </p>
         <p>wallabag is a self hostable application for saving web pages.</p>
         <p>#PHP</p>
      </td>
   </tr>
</table>

Will produce:

image

Field to add in repo table:

  • language
  • homepage

Installation error: 'cache:clear --no-warmup'

Hi there,
I have just tried to install banditore onto a new server and sadly I ran into the following problem:

  1. running SYMFONY_ENV=prod sudo -u www-data php /usr/local/bin/composer install -o --no-dev
  2. composer downloads all depedencies
  3. composer let me enter the parameters
  4. error occurs
> Sensio\Bundle\DistributionBundle\Composer\ScriptHandler::buildBootstrap
> Sensio\Bundle\DistributionBundle\Composer\ScriptHandler::clearCache
PHP Fatal error:  Uncaught Symfony\Component\Debug\Exception\ClassNotFoundException: Attempted to load class "DoctrineFixturesBundle" from namespace "Doctrine\Bundle\FixturesBundle".
Did you forget a "use" statement for another namespace? in /var/www/banditore.skl.de/app/AppKernel.php:33
Stack trace:
#0 /var/www/banditore.skl.de/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Kernel.php(493): AppKernel->registerBundles()
#1 /var/www/banditore.skl.de/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Kernel.php(132): Symfony\Component\HttpKernel\Kernel->initializeBundles()
#2 /var/www/banditore.skl.de/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Application.php(64): Symfony\Component\HttpKernel\Kernel->boot()
#3 /var/www/banditore.skl.de/vendor/symfony/symfony/src/Symfony/Component/Console/Application.php(148): Symfony\Bundle\FrameworkBundle\Console\Application->doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#4 /var/www/banditore in /var/www/banditore.skl.de/app/AppKernel.php on line 33
Script Sensio\Bundle\DistributionBundle\Composer\ScriptHandler::clearCache handling the symfony-scripts event terminated with an exception

  [RuntimeException]
  An error occurred when executing the "'cache:clear --no-warmup'" command:

  PHP Fatal error:  Uncaught Symfony\Component\Debug\Exception\ClassNotFoundException: Attempted to load class "DoctrineFixturesBundle" f
  rom namespace "Doctrine\Bundle\FixturesBundle".
  Did you forget a "use" statement for another namespace? in /var/www/banditore.skl.de/app/AppKernel.php:33
  Stack trace:
  #0 /var/www/banditore.skl.de/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Kernel.php(493): AppKernel->registerBundles()
  #1 /var/www/banditore.skl.de/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Kernel.php(132): Symfony\Component\HttpKernel\Kern
  el->initializeBundles()
  #2 /var/www/banditore.skl.de/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Application.php(64): Symfony\Component\H
  ttpKernel\Kernel->boot()
  #3 /var/www/banditore.skl.de/vendor/symfony/symfony/src/Symfony/Component/Console/Application.php(148): Symfony\Bundle\FrameworkBundle\
  Console\Application->doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
  #4 /var/www/banditore in /var/www/banditore.skl.de/app/AppKernel.php on line 33

I have tried it with the current master, version 3.0.0 and version 2.1.1
All with the same outcome.


My setup:

  • Ubuntu 18.04.01
  • PHP 7.2.24-0ubuntu0.18.04.1
  • Composer version 1.9.1 2019-11-01 17:20:17

Any idea what could be the cause?

Worker improvements

  • Consider installating the pecl extension (http://pecl.php.net/package/amqp)
  • Add some processors:
    • swarrot.processor.max_messages with 50 message
    • swarrot.processor.doctrine_connection to avoid MySQL has gone away (see #16)
    • swarrot.processor.new_relic but the free version of NewRelic doesn't allow to see background job...
    • maybe the option requeue_on_error on swarrot.processor.ack

Properly handle emoji in database

Some release message includes emoji (of course), like the release 0.6.1 of Medis:
image

The Github endpoint for convertir markdown to html convert it to:

<g-emoji alias="smiley" fallback-src="https://assets-cdn.github.com/images/icons/emoji/unicode/1f603.png" ios-version="6.0">๐Ÿ˜ƒ</g-emoji>

but it is saved like that in MySQL:

<g-emoji alias="smiley" fallback-src="https://assets-cdn.github.com/images/icons/emoji/unicode/1f603.png" ios-version="6.0">?</g-emoji>

Even if the database & table & field are set to utf8bm4 & utf8bm4_unicode_ci ๐Ÿ˜ž

{"collate"="utf8mb4_unicode_ci", "charset"="utf8mb4"}

Put all versions in the RSS feed

Right now, we only push version released after the account creation for the user.
We should put every thing we have in the RSS feed, no matter the creation date of the account.

Sometimes RateLimit generate SSL Error

https://sentry.io/wildtrip/banditore/issues/226720157/

GuzzleHttp\Exception\ConnectException: cURL error 35: SSL read: error:00000000:lib(0):func(0):reason(0), errno 104 (see http://curl.haxx.se/libcurl/c/libcurl-errors.html)
  File "vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php", line 186, in createRejection
    ? new ConnectException($message, $easy->request, null, $ctx)
  File "vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php", line 150, in finishError
    return self::createRejection($easy, $ctx);
  File "vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php", line 103, in finish
    return self::finishError($handler, $easy, $factory);
  File "vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php", line 180, in processMessages
    )
  File "vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php", line 108, in tick
    $this->processMessages();
...
(45 additional frame(s) were not displayed)

Http\Client\Exception\NetworkException: cURL error 35: SSL read: error:00000000:lib(0):func(0):reason(0), errno 104 (see http://curl.haxx.se/libcurl/c/libcurl-errors.html)
  File "vendor/php-http/guzzle6-adapter/src/Promise.php", line 121, in handleException
    return new HttplugException\NetworkException($exception->getMessage(), $exception->getRequest(), $exception);
  File "vendor/php-http/guzzle6-adapter/src/Promise.php", line 63, in Http\Adapter\Guzzle6\{closure}
    $this->exception = $this->handleException($reason, $request);
  File "vendor/guzzlehttp/promises/src/Promise.php", line 203, in callHandler
    $promise->resolve($handler[$index]($value));
  File "vendor/guzzlehttp/promises/src/Promise.php", line 174, in GuzzleHttp\Promise\{closure}
    self::callHandler(2, $reason, $handler);
  File "vendor/guzzlehttp/promises/src/RejectedPromise.php", line 40, in GuzzleHttp\Promise\{closure}
    $p->resolve($onRejected($reason));
...
(47 additional frame(s) were not displayed)

Remove PGP signature from tag / commit

Some tag are associated to a tag or a commit which can be signed.

For example:
https://api.github.com/repos/splitsh/lite/git/tags/695f8b5c1b81ba1d9109018b4f0e73436cb1a60c

{
    "sha": "695f8b5c1b81ba1d9109018b4f0e73436cb1a60c",
    "url": "https://api.github.com/repos/splitsh/lite/git/tags/695f8b5c1b81ba1d9109018b4f0e73436cb1a60c",
    "tagger": {
        "name": "Fabien Potencier",
        "email": "[email protected]",
        "date": "2017-02-25T02:11:11Z"
    },
    "object": {
        "sha": "260fd613557325063bf0df6ae691639c073772b1",
        "type": "commit",
        "url": "https://api.github.com/repos/splitsh/lite/git/commits/260fd613557325063bf0df6ae691639c073772b1"
    },
    "tag": "v1.0.1",
    "message": "created tag 1.0.1\n-----BEGIN PGP SIGNATURE-----\nVersion: GnuPG v2\n\niF4EABEIAAYFAliw58IACgkQ64qmmlZsB5VNFwD+L1M86cO76oohqSy4TCbubPAL\n6341glOKJpfkwyjQnUkBAPCTZSBbe8CFHLxLUvypIiQSMn+AIkPfvzvSEahA40Vz\n=SaF+\n-----END PGP SIGNATURE-----\n"
}

We should remove that signature to only keep revelant information about the tag.
In that case:

created tag 1.0.1

Handle error when user revoke its token

If a user revoke its token, the rate limit will always fail and generate too much errors.

[2017-03-30 13:10:06] app.ERROR: [Ack] An exception occurred. Message 1 has been nack'ed. {"swarrot_processor":"ack","exception":"[object] (Github\Exception\RuntimeException(code: 401): Bad credentials at /www/vendor/knplabs/github-api/lib/Github/HttpClient/Plugin/GithubExceptionThrower.php:87)"}

The full strace:

Github\Exception\RuntimeException: Bad credentials
33 vendor/knplabs/github-api/lib/Github/HttpClient/Plugin/GithubExceptionThrower.php(87): Github\HttpClient\Plugin{closure}
32 vendor/php-http/httplug/src/Promise/HttpFulfilledPromise.php(34): then
31 vendor/knplabs/github-api/lib/Github/HttpClient/Plugin/GithubExceptionThrower.php(88): handleRequest
30 vendor/php-http/client-common/src/PluginClient.php(156): Http\Client\Common{closure}
29 vendor/php-http/client-common/src/PluginClient.php(170): Http\Client\Common{closure}
28 vendor/php-http/client-common/src/PluginClient.php(87): sendRequest
27 vendor/php-http/client-common/src/HttpMethodsClient.php(203): sendRequest
26 vendor/php-http/client-common/src/HttpMethodsClient.php(193): send
25 vendor/php-http/client-common/src/HttpMethodsClient.php(61): get
24 vendor/knplabs/github-api/lib/Github/Api/AbstractApi.php(81): get
23 vendor/knplabs/github-api/lib/Github/Api/RateLimit.php(20): getRateLimits
22 src/AppBundle/Github/RateLimitTrait.php(20): getRateLimits
21 src/AppBundle/Github/ClientDiscovery.php(90): find
20 var/cache/prod/appProdProjectContainer.php(433): getBanditore_Client_GithubService
19 vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Container.php(310): get
18 var/cache/prod/appProdProjectContainer.php(474): getBanditore_Consumer_SyncStarredReposService
17 var/cache/prod/appProdProjectContainer.php(465): {closure}
16 var/cache/prod/appProdProjectContainer.php(5004): __invoke
15 var/cache/prod/appProdProjectContainer.php(5004): process
14 vendor/swarrot/swarrot/src/Swarrot/Processor/Ack/AckProcessor.php(47): process
13 vendor/swarrot/swarrot/src/Swarrot/Processor/ExceptionCatcher/ExceptionCatcherProcessor.php(33): process
12 vendor/swarrot/swarrot/src/Swarrot/Processor/Retry/RetryProcessor.php(32): process
11 vendor/swarrot/swarrot/src/Swarrot/Processor/Doctrine/ConnectionProcessor.php(71): process
10 vendor/swarrot/swarrot/src/Swarrot/Processor/MaxMessages/MaxMessagesProcessor.php(43): process
9 vendor/swarrot/swarrot/src/Swarrot/Processor/NewRelic/NewRelicProcessor.php(45): process
8 vendor/swarrot/swarrot/src/Swarrot/Processor/Stack/StackedProcessor.php(67): process
7 vendor/swarrot/swarrot/src/Swarrot/Consumer.php(80): consume
6 vendor/swarrot/swarrot-bundle/Command/SwarrotCommand.php(97): execute
5 vendor/symfony/symfony/src/Symfony/Component/Console/Command/Command.php(265): run
4 vendor/symfony/symfony/src/Symfony/Component/Console/Application.php(852): doRunCommand
3 vendor/symfony/symfony/src/Symfony/Component/Console/Application.php(189): doRun
2 vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Application.php(80): doRun
1 vendor/symfony/symfony/src/Symfony/Component/Console/Application.php(120): run
0 bin/console(28): null

only use public reposes for monitoring

would be great if there would be a way to use this service only for my public reposes.

I dont want to give away permission to access/review my private repositiories to "any tool"...

Add a way to know if starred repositories are currently processed

When you have starred a lot of repositories it could take some time to process all of them, so it could be usefull to inform user (through the dashboard) if his starred repositories are currently processed or not, and if it's done or not (like a Travis job, or something similar).

Bandito.re no longer updating

Hi there,

my Bantido.re feed got stuck a few days ago. It's no longer loading new releases of starred projects. There were for example new updates of Ansible or the Terraform AWS provider, but it's not showing up.

image

With best regards

If we "triggered an abuse detection mechanism", we should skip to the next repo

When we parse new repo for the first time, we can triggeer an abuse detection on Github for the markdown endpoint.

Failed to parse markdown: You have triggered an abuse detection mechanism. Please wait a few minutes before you try again.

Actually, we just skip the version to try on the next one, wich will fail to.
Instead, we should just skip the whole repo and jump to the next one.

Jump to GraphQL to save some requests?

Request to retrieve all information from tag & release for a given repository.

This one only use releases:

{
  repository(owner: "j0k3r", name: "graby") {
    tags: refs(refPrefix: "refs/tags/", first: 5, direction: DESC) {
      edges {
        tag: node {
          name
          target {
            ... on Tag {
              sha: oid
              message
              tagger {
                name
                date
              }
            }
          }
        }
      }
    }
    releases(last: 5) {
      nodes {
        id
        name
        description
        publishedAt
        tag {
          name
        }
      }
    }
  }
}

This one only use tags:

{
  repository(owner: "rails", name: "rails") {
    tags: refs(refPrefix: "refs/tags/", first: 5, direction: DESC) {
      edges {
        tag: node {
          name
          target {
            ... on Commit {
              message
              author {
                name
                date
              }
            }
            ... on Tag {
              message
              tagger {
                name
                date
              }
            }
          }
        }
      }
    }
    releases(last: 5) {
      nodes {
        id
        name
        description
        publishedAt
        tag {
          name
        }
      }
    }
  }
}

Request to retrieve starred repositories & all information

First page:

{
  user(login: "j0k3r") {
    starredRepositories(first: 50, orderBy: {field: STARRED_AT, direction: ASC}) {
      pageInfo {
        hasNextPage
        hasPreviousPage
        endCursor
      }
      edges {
        node {
          id
          name
          description
          owner {
            login
            avatarURL
          }
        }
      }
    }
  }
}

The second one (using the endCursor as after parameter and until hasNextPage is false):

{
  user(login: "j0k3r") {
    starredRepositories(first: 50, orderBy: {field: STARRED_AT, direction: ASC}, after: "Y3Vyc29yOjIwMTItMDEtMDRUMTE6MjA6NDgrMDE6MDA=") {
      pageInfo {
        hasNextPage
        hasPreviousPage
        endCursor
      }
      edges {
        node {
          id
          name
          description
          owner {
            login
            avatarURL
          }
        }
      }
    }
  }
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.