GitHubのgit://形式のURLは暗号化されていないのでサポートされなくなったらしい。

 

何かの手違いでローカルPCでbrew servicesが使えなくなった。(それ以外のbrewコマンドは使える状態)

原因は、git clone https://... が失敗するためだったぽい。https://でなくgit@github.com: <user-name>/<repository>.gitをURLに指定するとできた🎉🙌

そもそもgit clone https://できないのがよくなさそう😅

error文にもある通りhttps://github.blog/2021-09-01-improving-git-protocol-security-github/をみると、git://は暗号化されていないのでサポートされなくなったらしい。

$ brew services --help
==> Tapping homebrew/services
Cloning into '/usr/local/Homebrew/Library/Taps/homebrew/homebrew-services'...
fatal: remote error:
The unauthenticated git protocol on port 9418 is no longer supported.
Please see https://github.blog/2021-09-01-improving-git-protocol-security-github/ for more information.
Error: Failure while executing; `git clone https://github.com/Homebrew/homebrew-services /usr/local/Homebrew/Library/Taps/homebrew/homebrew-services --origin=origin --template=` exited with 128.
Error: Failure while executing; `/usr/local/bin/brew tap homebrew/services` exited with 1.


# homebrew-services がない!!!
$ ll /usr/local/Homebrew/Library/Taps/homebrew
total 0
drwxr-xr-x 5 kin29 admin 160 4 29 17:25 .
drwxr-xr-x 5 kin29 admin 160 4 22 16:56 ..
drwxr-xr-x 17 kin29 admin 544 2 16 12:43 homebrew-cask
drwxr-xr-x 17 kin29 admin 544 2 16 12:43 homebrew-core
 


# git clone https://..だと失敗したので git@github.comを使うようにした。
$ git clone git@github.com:Homebrew/homebrew-services.git /usr/local/Homebrew/Library/Taps/homebrew/homebrew-services --origin=origin --template=
Cloning into '/usr/local/Homebrew/Library/Taps/homebrew/homebrew-services'...
remote: Enumerating objects: 1985, done.
remote: Counting objects: 100% (494/494), done.
remote: Compressing objects: 100% (162/162), done.
remote: Total 1985 (delta 338), reused 434 (delta 324), pack-reused 1491
Receiving objects: 100% (1985/1985), 545.80 KiB | 751.00 KiB/s, done.
Resolving deltas: 100% (893/893), done.
 

$ ll /usr/local/Homebrew/Library/Taps/homebrew
total 0
drwxr-xr-x 5 kin29 admin 160 4 29 17:25 .
drwxr-xr-x 5 kin29 admin 160 4 22 16:56 ..
drwxr-xr-x 17 kin29 admin 544 2 16 12:43 homebrew-cask
drwxr-xr-x 17 kin29 admin 544 2 16 12:43 homebrew-core
drwxr-xr-x 13 kin29 admin 416 4 29 17:25 homebrew-services //入った!

# 使えるようになったーーー!!!
$ brew services --help
Usage: brew services [subcommand]

Manage background services with macOS' launchctl(1) daemon manager.

If sudo is passed, operate on /Library/LaunchDaemons (started at boot).
Otherwise, operate on ~/Library/LaunchAgents (started at login).

【超小ネタ】GitHubでよく使う検索方法

🗂ファイル名を指定して検索したい時

filename:file_name
ex.) filename:composer.json

 

🍙言語を指定して検索したい時

language:LANGUAGE
ex.) language:php

 

🔎ざっくりいろんな検索をしたいとき

https://grep.app/search

↑のサイトは、外部ライブラリを使っていて具体的な使い方を知りたくて他の人どうやって使ってるんだろ🤔って思った時によく使っています。

GitHubの検索より使いやすくて、目的のものを見つけれる気がします。

 

 

 

【超小ネタ】CircleCIへのssh接続にハマった

 

GitHubのCIにCircleCIを使っていて、途中でjobが失敗した時CIの中身見たくてsshで接続したくなりますよね💡
例えば、「ディレクトリ配置うまくいってるかなー」「このパッケージちゃんとインストールされてるかな」とか… 🤔

その時、わたしがハマったポイントとしては、、、

\ ssh接続に使うid_rsaはGitHubのもの /

ということでした!

 

1.Rerun Job with SSHする

 

2. 出力されたIPにssh接続する 👈ここでハマった

↑に表記されている通り$ ssh -p 64535 [IP]とするとpermission deniedになってしまい、ssh接続できずハマっていました。

$ ssh -p 64536 [IP]
The authenticity of host '[IP]:64536 ([IP]:64536)' can't be established.
ED25519 key fingerprint is SHA256:xxxxx.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '[IP]:64536' (ED25519) to the list of known hosts.
user@[IP]: Permission denied (publickey).

 

私のローカルでは、GitHubのid_rsaを~/.ssh/id_rsa_githubに置いてます。
そのため-i ~/.ssh/id_rsa_githubで指定してあげるのが正解でした😇

ssh -i ~/.ssh/id_rsa_github -p [発行されたport] [発行されたip]

 

Use the same SSH public key that you use for your VCS-provider (e.g., GitHub).
と親切に説明してくれてるのを、素直に受け止めればこんなことにはならなかったでしょう…

 

複数チケットサービス検索用ライブラリを作って、はじめてのpackagist登録

どうもー!
5月なのに暑いですね、そして梅雨こないですねー
宮崎にも来る気配なしです。

 

今日も音楽フェスに行くのですが、
皆さん、チケットを取る際にどのチケット販売サービスを使っていますか?
どういう基準で選んでますか?
ポイントだったり会員ランクなどてしょうか。

 

入手困難なバンドなんかだと、
私の経験でよくあるのがA,Bというサービスではsold outにもかかわらず、
Cでは残りわずかで残っていたりするのです。
ありがたく即購入するんですが、
この事実に辿りつくまで、
サービスAで検索→sold outを確認
→サービスBで検索→sold outを確認
→サービスCで検索→残りわずかを確認
→嬉し恥ずかし焦る気持ちを抑える
→無事ゲット!!!(ゴール)

ゴールに行くまで3サイトも順番に回るのです。
順番や早さによっては、
先に早く見ておけば取れたかもしれないチケットを
失ってしまうのです。
そんな自分の経験から、複数サービス
(現在チケットぴあ、eplus、ローチケ)を
一括検索できるライブラリを作りました!
スクレイピングを使ってるので、
リニューアルとかされると困りますw

Github→kin29/ticket-hunter

 

Packagistにも登録済みですので、

$ composer require kin29/ticket-hunter

からも取得可能でございます。

 

今回、Packagistへの登録を初めてしました!

こちらを参考にさせてもらいました。

https://qiita.com/niikunihiro/items/fbd696e506e734782d8f

6. GithubのサービスフックにPackagistを登録する

は、自動になったのか勝手に登録された気がしました。

 

 

使い方は、簡単です!

  • 使いたい販売サービス名(TicketPia/Eplus/LawsonTicketのどれか、複数指定可)
  • キーワード(「雨のパレード」など)

を渡すだけです。

<?php

try {
  $ticketVendors = new Kin29\TicketHunter\TicketHunter(['TicketPia', 'Eplus', 'LawsonTicket'], '雨のパレード');
  $ticketVendors->echoJson($ticketVendors->getList());
} catch (\Exception $e) {
    die($e->getMessage());
}

出力結果はこんな感じです。

{
  "TicketPia": [
    {
      "title": "GRAPEVINE/雨のパレード〔愛知〕",
      "date_time": "2019/8/2(金)",
      "pref_id": "23",
      "pref_name": "愛知県",
      "stage": "名古屋クラブクアトロ",
      "sale_method": "先行抽選",
      "sale_status": "近日抽選受付2019/6/8(土) 10:00 ~ 2019/6/10(月) 18:00",
      "link": "http://ticket.pia.jp/pia/ticketInformation.do?eventCd=1922500&rlsCd=&lotRlsCd=27947"
    },
    ...
  ],
  "Eplus": [
    {...省略...},
  ],
  "LawsonTicket": [
    {...省略...},
  ]
}

販売前の場合、販売開始日を取得できるといいな
と今気づきました、issueに入れておこう!
日付のフォーマットも入れてないな、、、入れよう。
アウトプットしてると気づくことて多いですよね、大切。

 

このライブラリを作っていた矢先、先を越されたかのように、
Freaxがリリースれました、、便利ですね。

内容が被っていますが、ライブラリはなさそうなので作り続けました^^;

 

今後は、このライブラリを使ってAPIを作りたいなとおもっています。
BearSundayでまた作りたいなあと思っております。
さらに、検索フォームをSymfonyで作ろうかと思っています。

 

怠けないように宣言しておきます!

では、森道市場2019行ってきます!

 

 

InterfaceとAbstractを使ってみる

「インターフェース」ってよく聞きますが、あんまりわかってないです。
というか全くわかりません、「インターフェース」って言いたいだけです。

PHP conference Kansai2018で
インターフェース再入門」というセッションがあり、
そこから気になりだしたので探ってみます。
会社で、PHP conference Kansai2018共有LTしたので、
もうちょい深いところつつけたらなと思います。

ちなみに過去に私が書いたPHP conference Kansai2018の記事はこちら

PHPカンファレンス関西へ初参戦!

インターフェースとは…

何か目的を果たすための手段となるもの。
中身を知らなくても使う側は使うことができる。

例えば…

ECサイト(「ブーメラン」を売っているお店とする)で考えると

  • 使い手 = 買い手
  • 目的 = 「ブーメラン」をクレジットカードで購入したい
  • 手段 = 決済モジュールのクレジットカード決済

→ この時のインターフェースは「決済モジュール」になります!

例えば…(パート2)

  • 使い手 = わたし
  • 目的 = 青い空の下で音楽を聞きながら、お酒飲みたい!
  • 手段 = フェスに行く

→ この時のインターフェースは「フェス」になります!

↑をPHPで書いてみます!
〜InterfaceとAbstract使って〜

実際に書いてたらabstractとの違いがわかんなくなってきたので、両方使ってみます。

参考:
https://havelog.ayumusato.com/develop/php/e166-php-interface-abstract.html

\  Github – kin29/fes  /

  • GoodFesInterface.php             ←Interface。
  • AbstractFes.php                        ←Abstract。(WildBunchFesの継承クラス)
  • WildBunchFes.php                  ← AbstractFesを継承し、GoodFesInterfaceを使用
  • Watashi.php                              ←わたし(使い手)

◎ 前提

「青い空の下で音楽を聞きながら、お酒飲みたい!」という、
わたしの願いを叶えるための良いフェスの必須条件を
GoodFesInterface.php」  にこめています。

さらに、わたしが今年の夏に楽しんだフェス
「WildBunchFes(通称WBF)」 「CircleFukuoka」 「まつり宮崎」 「AoshimaBeachParkFes」。
これら4つに共通するもの、
「音楽」 「酒」 「晴れ」 「野外」 「フェスの名前」 「思い出」を
AbstractFesクラス」 に詰め込んで各Fesで継承しています。

 

GoodFesInterface.php [Interface]

<?php

namespace Fes;

interface GoodFesInterface
{
    public function isMusic();

    public function isDrink();

    public function isSunny();

    public function isOutdoor();
}

 

AbstractFes.php [Abstract class]

<?php

namespace Fes;

abstract class AbstractFes
{
    public $fes_name;
    public $music;
    public $drink;
    public $place;
    public $weather;
    public $memory;

    public function __construct()
    {
        $this->place = '野外';
        $this->weather = '快晴';
        $this->memory = 'フェス最高 ^ω^';
    }

    /**
     * @return string
     */
    public function getFesName()
    {
        return $this->fes_name;
    }

    /**
     * @return bool
     */
    public function isMusic()
    {
        return isset($this->music);
    }

    /**
     * @return bool
     */
    public function isDrink()
    {
        return isset($this->drink);
    }

    /**
     * @return bool
     */
    public function isOutdoor()
    {
        return ($this->place === '野外');
    }

    /**
     * @return bool
     */
    public function isSunny()
    {
        return ($this->weather === '快晴');
    }

    /**
     * @return string
     */
    public function getMemory()
    {
        return $this->memory;
    }
}

 

WildBunchFes.php

<?php

namespace Fes;

class WildBunchFes extends AbstractFes implements GoodFesInterface
{
    public $fes_name;
    public $music;
    public $drink;
    public $memory;

    public function __construct()
    {
        parent::__construct();
        $this->fes_name = '「ワイルドバンチ」';
        $this->music = 'ヤバT/サカナクション/スカパラ/10-FEET/フォーリミ/teto/ハルカミライ...etc';
        $this->drink = 'ビール、ビール';
        $this->memory = 'しかし、2日目は台風直撃で中止になりました。';
    }
}

 

Watashi.php

<?php

namespace Bootstrap;
require dirname(__DIR__) . '/vendor/autoload.php';

use Fes\WildBunchFes;
...


class Wahatashi
{
    public $wbf;
    ...

    public function __construct()
    {
        $this->wbf = new WildBunchFes();
        ...
    }

    public function done()
    {
        $arrRet = array();
        $arrRet[get_class($this->wbf)] = $this->doneList($this->wbf);
        ...

        var_dump($arrRet);
    }

    public function doneList($class)
    {
        $arrRet = array();
        foreach ($this->arrDoList as $item){
            $arrRet[$item] = $class->{$item}();
        }
        return $arrRet;
    }
}

$i_am = new Wahatashi();
$i_am->done();

Watashi.phpは、
WildBunchFesクラスを通して、GoodFesInterfaceを使って(手段)として、
目的を果たしています。ですよね?w

 

まだまだ私の理解が乏しく、間違っている部分もあるかと思います。
そして、「こんなソースいつ使うんだ?」って感じですが、
自分の好きなものでソースを書くってなかなか面白かったです。

みなさんもぜひしてみてください! \(^^)/