HexoでGitHub flavored markdownを使う

 
カテゴリー Markdown SSG   タグ

hexo-renderer-marked

Install

npm install hexo-renderer-marked --save

Configuration

GitHub flavored markdown(gfm) を有効にする

_config.ymlに以下を追記

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
marked:
gfm: true
pedantic: false
breaks: true
smartLists: true
smartypants: true
modifyAnchors: 0
autolink: true
sanitizeUrl: false
headerIds: true
prependRoot: false
external_link:
enable: false
exclude: []
nofollow: false

GitHub flavored markdown

gfm - gfm task list items

  • foo
  • bar
  • foo
    • bar
    • baz
  • bim

breaks - gfm line breaks

効いていない……

1
2
Hello  (<-- two spaces)
World

Hello
World

1
2
Hello (<-- one spaces)
World

Hello
World

1
2
Hello (<-- no spaces)
World

Hello
World

autolink

1
https://hexo.io

https://hexo.io

Definition/Description Lists

1
2
3
4
5
6
Definition Term
: This is the definition for the term

Definition Term
: Definition 1
: Definition 2
Definition Term
This is the definition for the term
Definition Term
: Definition 1
Definition 2

コメント・シェア

Python loggingによるログ操作

 
カテゴリー Python   タグ

LogLevelをどう設定すべきか

行いたいタスク そのタスクに最適なツール
通常の操作中に発生したイベント logging.info()
診断のための詳細な出力 logging.debug()
特定のランタイムイベントに関わる警告(呼び出し側を修正すべき) warnings.warn()
特定のランタイムイベントに関わる警告 logging.warning()
特定のランタイムイベントに関わるエラー raise exception
例外の送出をしないエラーの抑制 logging.error(), logging.exception(), logging.ciritical()

getLogger()

getLogger()で一度生成されたloggerは同一プロセス内では1つの実体として動作している。loggingクックブック内ではモジュールのグローバル変数としてloggerを定義している。

logging.getLogger(‘someLogger’)の複数回の呼び出しは同じloggerへの参照を返します。これは同じPythonインタプリタプロセス上で動いている限り、一つのモジュールの中からに限らず、モジュールをまたいでも当てはまります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import logging

# create logger
module_logger = logging.getLogger('spam_application.auxiliary')

class Auxiliary:
def __init__(self):
self.logger = logging.getLogger('spam_application.auxiliary.Auxiliary')
self.logger.info('creating an instance of Auxiliary')

def do_something(self):
self.logger.info('doing something')
a = 1 + 1
self.logger.info('done doing something')

def some_function():
module_logger.info('received a call to "some_function"')

disable_existing_loggersでloggerが無効化される

logging.config.dictConfigで設定を読み込んくとすでに作成済のloggerが無効化されてしまう。無効化させないためにはdisable_existing_loggersFalseに設定する。

disable_existing_loggers.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import logging
import logging.config

logger = logging.getLogger(__name__)

# load config from file
# logging.config.fileConfig('logging.ini', disable_existing_loggers=False)
# or, for dictConfig
logging.config.dictConfig({
'version': 1,
'disable_existing_loggers': False, # this fixes the problem
'formatters': {
'standard': {
'format': '%(asctime)s [%(levelname)s] %(name)s: %(message)s'
},
},
'handlers': {
'default': {
'level':'INFO',
'class':'logging.StreamHandler',
'formatter':'standard'
},
},
'loggers': {
'': {
'handlers': ['default'],
'level': 'INFO',
'propagate': True
}
}
})

logger.debug('Debug log message')
logger.info('Info log message')
logger.warning('Warning log message')
logger.error('Error log message')

disable_existing_loggersがTrueの場合

ログが出力されない。

1
$python disable_existing_loggers.py

disable_existing_loggersがFalseの場合

期待通りのログが出力される。

1
2
3
4
$python disable_existing_loggers.py
2020-04-22 13:54:22,207 [INFO] __main__: Info log message
2020-04-22 13:54:22,209 [WARNING] __main__: Warning log message
2020-04-22 13:54:22,209 [ERROR] __main__: Error log message

loggerとdisable_existing_loggersの挙動をみる

logging_treeモジュール

loggerをツリー構造で見やすく表示する、logging_treeを使う。pip install logging_treeでインストールしておく。

my_module.py

テスト用のClassとFunctionでいくつかのLogLevelのメッセージを表示するモジュール。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import logging

def foo():
logger = logging.getLogger(__name__)
logger.debug('Debug log message/function')
logger.info('Info log message/function')
logger.warning('Warning log message/function')
logger.error('Error log message/function')

class Bar(object):
def __init__(self, logger=None):
self.logger = logger or logging.getLogger(__name__)

def bar(self):
self.logger.debug('Debug log message/method')
self.logger.info('Info log message/method')
self.logger.warning('Warning log message/method')
self.logger.error('Error log message/method')

main.py

テスト用のモジュールでログ出力とloggerのツリー構造を確認する。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import logging
import logging.config
import logging_tree

# load my module
import my_module

print("--------------------------------------------------------")
print("Step1: 標準のroot logger")
logging_tree.printout()
print("--------------------------------------------------------")

print("--------------------------------------------------------")
print("Step2: なにも設定せずgetLogger()を呼ぶ")
my_module.foo()
bar = my_module.Bar()
bar.bar()
print("")
print("getLogger()によってmy_moduleというloggerが生成されている")
logging_tree.printout()
print("--------------------------------------------------------")

print("--------------------------------------------------------")
print("Step3: logging.ini / disable_existing_loggers=True")
logging.config.fileConfig('logging.ini', disable_existing_loggers=True)
my_module.foo()
bar = my_module.Bar()
bar.bar()
print("")
print("すでにStep1でmy_module loggerは作成済だったので、無効化されている")
logging_tree.printout()
print("--------------------------------------------------------")

print("--------------------------------------------------------")
print("Step4: logging.ini / disable_existing_loggers=False")
logging.config.fileConfig('logging.ini', disable_existing_loggers=False)
my_module.foo()
bar = my_module.Bar()
bar.bar()
print("")
print("disable_existing_loggers=Falseで有効化されている")
logging_tree.printout()
print("--------------------------------------------------------")

logging.ini

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[loggers]
keys=root

[handlers]
keys=consoleHandler

[formatters]
keys=simpleFormatter

[logger_root]
level=DEBUG
handlers=consoleHandler

[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)

[formatter_simpleFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
datefmt=

main.py実行結果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
$python main.py
--------------------------------------------------------
Step1: 標準のroot logger
<--""
Level WARNING
--------------------------------------------------------
--------------------------------------------------------
Step2: なにも設定せずgetLogger()を呼ぶ
Warning log message/function
Error log message/function
Warning log message/method
Error log message/method

getLogger()によってmy_moduleというloggerが生成されている
<--""
Level WARNING
|
o<--"my_module"
Level NOTSET so inherits level WARNING
--------------------------------------------------------
--------------------------------------------------------
Step3: logging.ini / disable_existing_loggers=True

すでにStep1でmy_module loggerは作成済だったので、無効化されている
<--""
Level DEBUG
Handler Stream <_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>
Level DEBUG
Formatter fmt='%(asctime)s - %(name)s - %(levelname)s - %(message)s' datefmt=''
|
o<--"my_module"
Level NOTSET so inherits level DEBUG
Disabled
--------------------------------------------------------
--------------------------------------------------------
Step4: logging.ini / disable_existing_loggers=False
2020-04-04 22:03:42,281 - my_module - DEBUG - Debug log message/function
2020-04-04 22:03:42,281 - my_module - INFO - Info log message/function
2020-04-04 22:03:42,281 - my_module - WARNING - Warning log message/function
2020-04-04 22:03:42,281 - my_module - ERROR - Error log message/function
2020-04-04 22:03:42,281 - my_module - DEBUG - Debug log message/method
2020-04-04 22:03:42,281 - my_module - INFO - Info log message/method
2020-04-04 22:03:42,281 - my_module - WARNING - Warning log message/method
2020-04-04 22:03:42,281 - my_module - ERROR - Error log message/method

disable_existing_loggers=Falseで有効化されている
<--""
Level DEBUG
Handler Stream <_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>
Level DEBUG
Formatter fmt='%(asctime)s - %(name)s - %(levelname)s - %(message)s' datefmt=''
|
o<--"my_module"
Level NOTSET so inherits level DEBUG
--------------------------------------------------------

Pythonの例外処理とトレースバック

Pythonの例外処理をCatchして、ログ出力する。StuckTraceは改行を含む複数行の内容になるので、JSON形式でのログ出力を行う。JSON形式でログ出力するために、jsonloggerを使うのでpip install python-json-loggerでインストールする。

複数行の例外は読みやすいですが、外部のログサービスを使用してログを集計している場合は、ログをJSONに変換して、複数行のログが正しく解析されるようにする必要があります。

lowermodule.py

  • 例外をキャッチしてスタックトレースをログに記録する
  • スタックトレースは複数行になるので、JSON形式で扱いやすく出力する
  • JSONにはカスタム項目(extra=)を追記することも可能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# lowermodule.py
import logging.config
import traceback
import time
from pythonjsonlogger import jsonlogger
import logging_tree

def word_count(myfile):
#logger = logging.getLogger(__name__)
logger = logging.getLogger('lowermodule')
logging.config.fileConfig('logging.json.ini', disable_existing_loggers=False)
print(logger.name)
logging_tree.printout()
try:
starttime = time.time()
with open(myfile, 'r') as f:
file_data = f.read()
words = file_data.split(" ")
final_word_count = len(words)
endtime = time.time()
duration = endtime - starttime
logger.info("this file has %d words", final_word_count, extra={"run_duration":duration})
return final_word_count
except OSError as e:
logger.error(e, exc_info=True)
except:
logger.error("uncaught exception: %s", traceback.format_exc())
return False

if __name__ == '__main__':
word_count('myfile.txt')

logging.json.ini

__name__lowermoduleの場合にconsoleとjsonに出力する。consoleはroot loggerの定義によって、jsonはlowermodule loggerの定義によって出力される。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
[loggers]
keys=root,lowermodule

[handlers]
keys=consoleHandler,fileHandler

[formatters]
keys=simpleFormatter,json

[logger_root]
level=DEBUG
handlers=consoleHandler

[logger_lowermodule]
level=DEBUG
handlers=fileHandler
qualname=lowermodule

[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)

[handler_fileHandler]
class=FileHandler
level=DEBUG
formatter=json
args=("myapp.log",)

[formatter_json]
class=pythonjsonlogger.jsonlogger.JsonFormatter
format=%(asctime)s %(name)s %(levelname)s %(message)s

[formatter_simpleFormatter]
format=%(asctime)s %(name)s - %(levelname)s:%(message)s

lowermodule.py実行結果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$python lowermodule.py
lowermodule
<--""
Level DEBUG
Handler Stream <_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>
Level DEBUG
Formatter fmt='%(asctime)s %(name)s - %(levelname)s:%(message)s' datefmt=None
|
o<--"lowermodule"
Level DEBUG
Handler File '/work/logging_traceback/myapp.log'
Level DEBUG
Formatter <pythonjsonlogger.jsonlogger.JsonFormatter object at 0x7f41b0c0e040>
2020-04-05 01:12:37,312 lowermodule - ERROR:[Errno 2] No such file or directory: 'myfile.txt'
Traceback (most recent call last):
File "lowermodule.py", line 16, in word_count
with open(myfile, 'r') as f:
FileNotFoundError: [Errno 2] No such file or directory: 'myfile.txt'

$cat myapp.log
{"asctime": "2020-04-05 01:12:37,312", "name": "lowermodule", "levelname": "ERROR", "message": "[Errno 2] No such file or directory: 'myfile.txt'", "exc_info": "Traceback (most recent call last):\n File \"lowermodule.py\", line 16, in word_count\n with open(myfile, 'r') as f:\nFileNotFoundError: [Errno 2] No such file or directory: 'myfile.txt'"}

コメント・シェア

GitHubチュートリアル

 
カテゴリー Git Markdown SSG Tutorial   タグ

GitHub Guides

GitHub公式のチュートリアル。シンプルにまとまっている。

Hello World (ブランチ作成~Pull Request~Merge)

Web UIを使ったブランチ作成 ~ Pull Request ~ Merge操作。
masterからBranch ofしてPull Request ~ Mergeする、簡単なGitHub flowプロセスチュートリアル。ブランチの仕組みを理解してもらうのに良いチュートリアル。

Step 1. Create a Repository

リポジトリを作成する。

Step 2. Create a Branch

リポジトリの画面でbranch: masterからSwitch branch/tagsを選択して、readme-editsをいれてCreate branchをクリックして作成。

  • デフォルトでは、masterがあり、これがもっとも信頼できるブランチ
  • ブランチを使用して、masterへコミットする前にテストを行う

Step 3. Make and commit changes

README.mdを画面上から修正。Commitコメントなども記入。Commit先がreadme-editsになっていることを確認したうえで、Commit change

Step 4. Open a Pull Request

pull requestsタブでNew pull request
base: mastercompare: readme-editsにするとAble to merge. These branches can be automatically merged.が表示される。

併せて表示されたCreate pull requestをクリック。表示された画面で、pull requestのtitleやメッセージを記入した上で、Create pull requestをクリック。

Step 5. Merge your Pull Request

pull requestsタブでにStep4で作成したPull Requestが表示されている。

画面下部からチャットメッセージを投稿することができる。

リクエストを承認するならMerge pull requestをクリックしてマージする。

マージした後はPull request successfully merged and closedという内容が表示されているので、Delete branchでブランチを削除する。

Forking Projects (Fork~Pull Request)

リポジトリをForkしてPull Requestまでの操作を行うチュートリアル
細かい操作の説明はないので、Git操作は知っている前提になっている。

Step1. Fork the repository

Spoon-Knifeリポジトリの右上にあるForkボタンを押してプロジェクトをフォークする。

Forking octocat/Spoon-Knifeというメッセージの画面で少し待つとリポジトリのフォークが完成する。

Step2. Clone your fork

フォークしたプロジェクトをローカルにクローンする。

  • originhttps://www.github.com/<your_username>/Spoon-Knife
  • upstreamをオリジナルのSpoon-Knifeにする。

Step3. Making a Pull Request

ローカルでindex.htmlファイルを編集してpushする。

  • stage: git addで追加する
  • local repository: git commitでcommitする。
  • remote repository: git pushでpushする。

Step4. Making a Pull Request

https://www.github.com/<your_username>/Spoon-Knifepull requestsタブでNew pull request

base repository: octcat/Spoon-Knife:base:masterhead repository: <your_username>/Spoon-Knife:compare masterにするとAble to merge. These branches can be automatically merged.が表示される。

併せて表示されたCreate pull requestをクリック。表示された画面で、pull requestのtitleやメッセージを記入した上で、Create pull requestをクリック。

Mastering Issues

GitHub Flavored Markdownのチュートリアル。

Mastering Markdown (GitHub Flavored Markdown)

Syntax highlighting

1
2
3
4
5
function fancyAlert(arg) {
if(arg) {
$.facebox({div:'#foo'})
}
}

Task Lists

1
2
3
4
- [x] @mentions, #refs, [links](), **formatting**, and <del>tags</del> supported
- [x] list syntax required (any unordered or ordered list supported)
- [x] this is a complete item
- [ ] this is an incomplete item
  • @mentions, #refs, links, formatting, and tags supported
  • list syntax required (any unordered or ordered list supported)
  • this is a complete item
  • this is an incomplete item

Tables

1
2
3
4
First Header | Second Header
------------ | -------------
Content from cell 1 | Content from cell 2
Content in the first column | Content in the second column
First Header Second Header
Content from cell 1 Content from cell 2
Content in the first column Content in the second column

Getting Started with GitHub Pages

GitHub Pagesのチュートリアル。

Step1. Create Your Website

username.github.ioという名前のリポジトリを作成。usernameはGitHubのアカウント。

GitHub Pages width=640

Settings -> GitHub PagesChose themeからテーマを選択。

GitHub Pages width=640

GitHub Pages width=640

Jeykllで静的ページを作成するため、元になるindex.mdファイルを作成。

GitHub Pages width=640

編集したら、Commitコメントを入れて Commit changes

GitHub Pages width=640

username.github.ioにアクセスするとページが表示される。

GitHub Pages width=640

Step2. Making Changes

自動生成されている_config.ymlを編集してページを更新する。

GitHub Pages width=640

テーマ選択だけが設定されている。

GitHub Pages width=640

titledescriptionを追記。

GitHub Pages width=640

ページが更新される。

GitHub Pages width=640

Step3. リポジトリ毎のページ

リポジトリ用のページusername.github.io/<reponame>でアクセスできる。作成方法は同じ。

GitHub Pages width=640

Settings -> GitHub Pagessourceからmaster branchを選択。

GitHub Pages width=640

GitHub Pages width=640

Jeykllで静的ページを作成するため、元になるindex.mdファイルを作成。

GitHub Pages width=640

GitHub Pages width=640

Settings -> GitHub PagesChose themeからテーマを選択。

GitHub Pages width=640

テーマを選択。

GitHub Pages width=640

コメント・シェア

GitできれいなPull Requestを送る

 
カテゴリー Git   タグ

How to write the perfect pull request

Approach to writing a Pull Requestの要約

  • Pull Requestの目的を含める
  • 作業が行われている理由の概要(経緯に精通していることを求めない)
  • 誰もがこのPull Requestを読んでいる可能性があることを忘れない
  • 必要なフィードバックがある場合はそれについて明確に説明
  • Pull Requestが進行中の場合はフィードバックが必要な時期を明記し[WIP]などのタイトル接頭辞を付ける
  • ディスカッションに参加させたい@メンションとその理由を記載
    • 例)/cc @GitHub/security、このアプローチに関する懸念は?

Offering feedbackの要約

  • 問題の文脈とこのPull Requestが存在する理由をよく理解する
  • 同意できない場合、応答する前に数分検討してから
  • Ask, dont’ tell. (“できない”より”なんでそれをするの?”)
  • コードを変更する理由を説明
  • コードを簡略化または改善する方法を提供
  • 中傷的な用語を使用しない
  • 謙虚になる
  • 誇張を避ける
  • グループの批評を通じて、専門的なスキル、グループの知識、製品の品質向上を目指す
  • オンラインコミュニケーションのマイナスバイアスに注意
  • 絵文字を使用してトーンを明確にする

Responding to feedbackの要約

  • 説明を求める
  • 問題解決のために行った決定を説明する
  • すべてのコメントに返信する
  • Followup Commit、またはPull Requestへのリンク
  • 議論が混乱している場合、オフラインで議論し、まとめたフォローアップを投稿することも検討

Pull Request Hell

The Right Way / Being the Creatorの要約

  • Keep an open mind: 建設的な批評の必要がある、そしてそれがPRのすべて
  • Stay focused on your goal: 目的に集中し、大きなリファクタリングをしない
  • Document non trivial lines of code: 重要なコードにドキュメントを
  • Write unit tests for all your changes: PRとともにテストコードを
  • Finally read your code: 最後にコードを読んで簡素化・改善を行う

The Right Way / Being the Approverの要約

  • Make time to review the code: コードを確認する時間を確保
  • Write your own notes before using the tool: ツールを使う前にメモで考えを整理する
  • Make sure you know your standards: 自分の基準を知る
  • Learning, I think this is the most important part of being an approver: 学ぶことがもっとも重要

The Wrong Wayの要約

  • Writing comments just to write comments: コメントを書くためだけのコメント
  • Not learning: 学ばない
  • Creating a backdoor: 自分のコードを承認するバックドアを作る
  • Constantly commenting on Syntax and Styling: コーディング規約コメントばかりしている

10 tips for better Pull Requests

10 tips for better Pull Requestsの要約

  1. Make it small: 小さく焦点を絞ったPull Request/1ダース以下のファイル変更なら悪くない
  2. Do only one thing: 1つのPull Requestは1つのテーマのみ扱う、or Welcome to Merge Hell
  3. Watch your line width: レビュアーがdiffしやすいように
  4. Avoid re-formatting: フォーマットを変更したければ別Pull Requestで
  5. Make sure the code builds: 最低でも動くものを
  6. Make sure all tests pass: 最低でも自動テストをパスするものを
  7. Add tests: テストの追加も
  8. Document your reasoning: 理由を書く
    • 「何を」書いたのかではなく、「なぜ」その方法でコードを書いたのかを説明
  9. Write well: 正しく作文する
  10. Avoid thrashing: スラッシング(無意味なコミット履歴)を避ける

Document your reasoningの要約

  1. Self-documenting code: 自己文書化したコード
  2. Code comments: うまく自己文書化できなかったらコメントで
  3. Commit messages: コードに書くことがふさわしくない内容を
  4. Pull Request comments: 自己文書化したコード

コメント・シェア

Google AdSense

Google AdSenseにサイトを登録する

google-adsense width=640

審査を受ける(失敗した例)

指定されたAdSenseコードをサイトに貼り付けて閲覧可能にする

google-adsense width=640

google-adsense width=640

サイトの修正

google-adsense width=640

コンテンツが存在しないはページがクロールされていないため。Google Search ConsoleでGoogleのクロールが完了してインデックス化されたことを確認したうえで再登録を実施する。
また、必須コンテンツとしてプライバシーポリシーの掲載が求められているので、プライバシーポリシーページを作成。

  • Google Search Consoleに登録し、Search Consoleのカバレッジが表示されるまで待つ
  • プライバシーポリシーを作成しサイトから閲覧できるようにする

再審査を受ける

google-adsense width=640

サイトを審査できません
新型コロナウイルス感染症 (COVID-19) の世界的流行の影響で、Google では現在、一部のサービスで一時的に遅延が発生しています。そのため、現時点ではお客様のサイトを審査することができません。

COVID19の影響がここまで……

審査合格

なんども同じエラーを繰り返し、6月にようやく審査完了。

google-adsense complete width=640

google-adsense complete width=640

google-adsense complete width=640

google-adsense complete width=640

google-adsense complete width=640

ads.txt

google-adsense complete width=640

ads.txtをダウンロードしてドメインのルートディレクトリに配置。警告メッセージはすぐには消えないが、ads.txt配置後しばらくすると消える。

自動広告の有効化

Google Adsense用のタグが表示されるので、サイトに貼り付ける。
内容は審査時のものと同じ。

google-adsense complete width=640

自動広告を有効化する。
サマリーのペンシルマークからサイトの広告設定が可能。

google-adsense complete width=640

google-adsense complete width=640

google-adsense complete width=640

コメント・シェア

Google Analytics

Google Analyticsにサイトを登録する

サイトを登録する

google-analytics width=640

今回はWebサイトなので、ウェブを選択。

google-analytics width=640

サイトのURLを設定。

google-analytics width=640

利用規約に同意して登録完了。

google-analytics width=640

トラッキングコードを取得する

管理 -> トラッキング情報からサイトに埋め込むトラッキングコードを確認する。

google-analytics width=640

google-analytics width=640

サイトのアクセス状況を把握する

情報が収集されると、ホーム画面でトラフィック状況を確認できる。

google-analytics width=640

ロケール別の統計。

google-analytics width=640

国別の統計。

google-analytics width=640

ブラウザ別の統計。

google-analytics width=640

OS別の統計。

google-analytics width=640

コメント・シェア

HexoにGoogle AdSenseを組み込む

 
カテゴリー Google SSG   タグ

審査を受ける

指定されたAdSenseコードをサイトに貼り付けて閲覧可能にする

themes/tranquilpeak/layout_partial/google_adsense.ejsを作成。

1
<script data-ad-client="ca-pub-xxxxxxxxxxxxxxxxx" async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>

themes/tranquilpeak/layoutlayout.ejsを修正してトップページ(フッター部分)に指定のコードが表示されるようにする

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<div id="blog">
<%- partial('_partial/header', {sidebarBehavior: sidebarBehavior}) %>
<%- partial('_partial/sidebar', {sidebarBehavior: sidebarBehavior}) %>
<%- partial('_partial/post/header-cover', {post: page, sidebarBehavior: sidebarBehavior}) %>
<div id="main" data-behavior="<%= sidebarBehavior %>"
class="<%= (page.coverImage ? 'hasCover' : '') %>
<%= (page.coverMeta === 'out' ? 'hasCoverMetaOut' : 'hasCoverMetaIn') %>
<%= (page.coverCaption ? 'hasCoverCaption' : '') %>">
<%- body %>
<!-- ここから -->
<%- partial('_partial/google-adsense') %>
<!-- ここまで -->
<%- partial('_partial/footer', null, {cache: !config.relative_link}) %>
</div>
<% if (is_post() && (page.actions === undefined || page.actions)) { %>
<div id="bottom-bar" class="post-bottom-bar" data-behavior="<%= sidebarBehavior %>">
<%- partial('_partial/post/actions', {post: page}) %>
</div>
<%- partial('_partial/post/share-options', {post: page, sidebarBehavior: sidebarBehavior}) %>
<% } %>
</div>

広告を表示させる

google_adsense.ejsの内容は同じなのでそのまま使用。

サイトの タグの間に AdSense コードをコピーして貼り付けます

と指定されているので、themes/tranquilpeak/layout_partial/head.ejs修正して以下のコードを追加。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
…略…
<!--STYLES END-->
<%- partial('google-analytics') %>
<!-- ここから -->
<%- partial('google-adsense') %>
<!-- ここまで -->
<%- partial('baidu-analytics') %>

<% if (page.comments) { %>
<% if (theme.gitment.enable) { %>
<%- css('assets/css/gitment.css') %>
<% } else if (theme.gitalk.enable) { %>
<%- css('assets/css/gitalk.css') %>
<% } %>
<% } %>
</head>

各ページのヘッダー部とフッター部にgoogle-adsense.ejsの内容が挿入されていることを確認。

コメント・シェア

.sshフォルダーをOneDriveに置く

OneDriveの直下に.sshフォルダーを設置する場合
フォルダーのシンボリックリンクを作成

1
2
C:\WINDOWS\system32>mklink /D C:\Users\ユーザ名\.ssh C:\Users\ユーザ名\OneDrive\.ssh
C:\Users\ユーザ名\.ssh <<===>> C:\Users\ユーザ名\OneDrive\.ssh のシンボリック リンクが作成されました

ssh-keygenでキーペアを作成する

ssh-keygenでキーペアを作成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
PS > ssh-keygen -t rsa -b 4096 -f host-key
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in host-key.
Your public key has been saved in host-key.pub.
The key fingerprint is:
SHA256:****************************************** user@host
The key's randomart image is:
+---[RSA 4096]----+
| o .. |
| o .. |
|.. E |
|. . . . |
| . + . S+. o |
|. o o *=*+= |
| . . .o+O=+. |
| =O+*= . |
| o=**+o+ |
+----[SHA256]-----+

.ssh/configのログイン設定

ssh-keygenで作成した公開鍵を接続先hostの.ssh/authorized_keysとして保存

OneDriveの.sshフォルダー内でhost.example.comフォルダーを作成しその中に秘密鍵host-keyを保存

1
2
3
4
5
6
7
8
#
# host.example.com
#
Host host host.example.com
HostName host.example.com
Port 10022
User myuser
IdentityFile ~.ssh/host.example.com/host-key

ここまで設定すればsshの基本操作は略名hostを使って、ポートやユーザ名指定なしで行える

  • shellアクセスやssh host
  • scpを使ったファイル転送scp host:~/remote_file.tst .

.ssh/configでPortforwardingを設定

host.example.comから接続可能なローカルホスト192.168.1.1と192.168.1.2に対してリモートデスクトップ接続を行う場合

LocalForwardを使ってPortforwardingを行う。
複数設定する場合は複数行設定すればいい。

1
2
3
4
5
6
7
8
9
10
#
# host.example.com
#
Host host host.example.com
HostName host.example.com
Port 10022
User myuser
LocalForward 13389 192.168.1.1:3389
LocalForward 23389 192.168.1.2:3389
IdentityFile ~.ssh/host.example.com/host-key

sshコマンドで同じことを実行するなら

1
ssh -L 13389:192.168.1.1:23389 55333:192.168.1.2:3389 myuser@host.example.com -p 10022

Portfowarding経由でリモートデスクトップに接続する

  1. ターミナル上でssh hostあるいはssh host.example.com
  2. リモートデスクトップでlocalhost:13389に接続すると192.168.1.1に接続
  3. リモートデスクトップでlocalhost:23389に接続すると192.168.1.2に接続

ただし、ターミナルを起動しておく必要があるので、sshはバックグラウンド実行する。

ssh -C -N -f host(-C:圧縮、-N:コマンド実行しない、-f:バックグラウンド)のようにオプションを組み合わせてバックグラウンドで実行するが、
Windows 10ではバックグラウンドにならないので、Start-Job { & ssh -C -N -f host }でバックグラウドジョブとして実行。

1
2
3
4
5
6
7
8
9
10
11
PS > Start-Job { & ssh -C -N -f host }

Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
1 Job1 BackgroundJob Running True localhost & ssh -C -N -f host

PS > Get-Job

Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
1 Job1 BackgroundJob Running True localhost & ssh -C -N -f host

このままだと長いのでエイリアスとして登録する

1
2
3
4
5
6
7
8
9
PS > New-Item -type file -force $profile

ディレクトリ: C:\Users\ユーザー名\Documents\WindowsPowerShell

Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2020/04/01 20:37 0 Microsoft.PowerShell_profile.ps1

PS > notepad $profile

エイリアス設定は以下。上記のジョブをctというコマンドで実行する。

1
2
function ConnectTunnel { Start-Job { & ssh -C -N -f host } }
Set-Alias ct ConnectTunnel

実行すると

1
2
3
4
5
6
7
PS > ct

Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
1 Job1 BackgroundJob Running True localhost & ssh -C -N -f host

PS >

エイリアスも共有する

OneDriveに共通のエイリアス定義をおいて複数のPCで共有する。

$profileC:\Users\ユーザ名\Documents\WindowsPowerShellフォルダーに生成されるので、これをOneDrive上に置く。
現在作成されているファイルをOneDrive上に移動して、フォルダーをシンボリックリンクで置き換える。

1
2
mklink /D C:\Users\ユーザ名\Documents\WindowsPowerShell C:\Users\ユーザ名\OneDrive\WindowsPowerShell
C:\Users\ユーザ名\Documents\WindowsPowerShell <<===>> C:\Users\ユーザ名\OneDrive\WindowsPowerShell のシンボリック リンクが作成されました

コメント・シェア

GitHub ActionsによるCI/CD

 
カテゴリー AWS Azure CI/CD GCP Git   タグ

GitHub Actions

GItHubのbuilt-in CI/CDツール。

GitHub Actions では、エンドツーエンドの継続的インテグレーション (CI) と継続的デプロイメント (CD) 機能をリポジトリに直接ビルドすることができます

Containerを実行することができる。

ワークフローは、Linux、macOS、Windows、コンテナで、’ランナー’と呼ばれるGitHubがホストするマシン上で実行できます

独自ホストでも動作する。

自分が所有もしくは管理するマシン上でワークフローを実行するために、独自にランナーをホストすることもできます

アクションのエコシステムと公開リポジトリのコンテナイメージを利用することができる。

ワークフローの作成には、リポジトリで定義されているアクション、GitHubのパブリックリポジトリにあるオープンソースのアクション、または公開されているDockerコンテナイメージを使用できます。フォークされたリポジトリのワークフローは、デフォルトでは動作しません。

すべてのリポジトリでGitHub Actionsはデフォルトで有効

By default, GitHub Actions is enabled on all repositories.

課金

Public repogitory

無料!

Private repository

デフォルトでは無料枠を越えると利用できない設定になっている。

デフォルトでは料金の上限は$0になっており、この上限に達した後に追加で分やストレージが使われないようになっています

無料枠

GitHubプラン ストレージ Minutes (per month)
GitHub Free 500 MB 2,000
GitHub Pro 1 GB 3,000

同時実行制限

GitHubプラン 最大同時ジョブ 最大同時macOSジョブ
GitHub Free 20 5
GitHub Pro 40 5

ワークフロー

.github/workflowsに保存する

1つのリポジトリに複数のワークフローを作成できます。 ワークフローは、リポジトリのルートにある.github/workflowsディレクトリに保存する必要があります。

YAMLで記述する

ワークフローは YAML 構文で設定し、リポジトリ内にワークフローファイルとして保存する必要があります。

ワークフローファイルの作成

  1. .github/workflowsという名前のディレクトリを作成
  2. .github/workflowsに、ワークフローのため.ymlまたは.yamlファイルを追加
  3. YAMLファイルをカスタマイズ(トリガーイベントとアクション)
  4. ワークフローを実行するブランチにコミット

トリガーイベント

PushやPull RequestのWeb Hookをトリガーに設定できる。

GitHub 上のアクティビティから webhook イベントが作成された際にワークフローを実行するよう設定できます。 ワークフローは、ワークフローの実行をトリガーするための webhook イベントを複数使用できます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
on:
# プッシュもしくはプルリクエストでワークフローを起動する
# ただしmasterブランチに対してのみ
push:
branches:
- master
pull_request:
branches:
- master
# page_buildとリリース作成イベントでも起動
page_build:
release:
types: # この設定は上のpage_buildイベントには影響しない
- created

Pull RequestのOpen/Closeをトリガーにした記述できる。

たとえば、プルリクエストが assigned、opened、synchronize、または reopened だったときにワークフローを実行できます。

1
2
3
on:
pull_request:
types: [assigned, opened, synchronize, reopened]

同一リポジトリ内でファイルマッチを指定してトリガーできる。

push および pull_request イベントを使用する場合、1 つ以上の変更されたファイルが paths-ignore にマッチしない場合や、1 つ以上の変更されたファイルが、設定された paths にマッチする場合にワークフローを実行するように設定できます。

1
2
3
4
on:
push:
paths-ignore:
- 'docs/**'
1
2
3
4
on:
push:
paths:
- '**.js'
1
2
3
4
5
on:
push:
paths:
- 'sub-project/**'
- '!sub-project/docs/**'

タスクスケジューリングも可能。

POSIX クーロン構文を使用して、特定の UTC 時間にワークフローを実行できるようスケジュール設定できます。 スケジュールしたワークフローは、デフォルトまたはベースブランチの直近のコミットで実行されます。 スケジュールされたワークフローを実行できる最短のインターバルは5分ごとです。

1
2
3
4
on:
schedule:
# * はYAMLに置ける特殊文字なので、この文字列は引用符で囲まなければならない
- cron: '*/15 * * * *'

ランナー

ジョブの実行環境を選択できる。

Linux、Windows、macOSなど、タイプとバージョンの異なる仮想ホストマシンを選択できます。 ワークフロー内の各ジョブは仮想環境の新しいインスタンスで実行され、1つのジョブ内のステップはファイルシステムを使って情報を共有できます。

1
runs-on: ubuntu-latest

ランナーの環境は/virtual-environmentsで確認することができる。

ビルドマトリクスの設定

複数のランタイムを記述できる。

複数のオペレーティングシステム、プラットフォーム、言語バージョンにまたがって同時にテストするときは、ビルドマトリクスを設定できます。

1
2
3
4
5
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-16.04, ubuntu-18.04]
node: [6, 8, 10]

チェックアウトアクション

チェックアウトアクションを使ってリポジトリのコードをコピーする。

リポジトリをビルドしてテストするとき、または継続的インテグレーションを使用するとき、ワークフローにリポジトリのコードのコピーが必要な場合。

1
- uses: actions/checkout@v2

DockerHubのContainerを参照する

DockerHubで公開されているコンテナイメージを利用することができる。

Dockerハブで公開されているDockerコンテナイメージで定義されているアクションは、ワークフローファイルのdocker://{image}:{tag}構文を使用して参照する必要があります。 コードとデータを保護するには、ワークフローで使用する前にDocker HubからのDockerコンテナイメージの整合性を確認してください。

1
2
3
4
5
jobs:
my_first_job:
steps:
- name: My first step
uses: docker://alpine:3.8

アクションを定義して再利用することもできる。

ワークフローのステータスバッジをリポジトリに追加する

ワークフローがnameキーワードを使用している場合、ワークフローは名前で参照しなければなりません。 ワークフローの名前に空白文字が含まれている場合、その文字をURLエンコードした文字列「%20」に置換する必要があります。

https://github.com/<OWNER>/<REPOSITORY>/workflows/<WORKFLOW_NAME>/badge.svg

Actionによるパブリッククラウド連携

主要なパブリッククラウドプロバイダーが公式のモジュールを公開している。

AWS

Azure

Google Cloud Platform

コメント・シェア



nullpo

めも


募集中


Japan