tohokuaikiのチラシの裏

技術的ネタとか。

Debianを10(buster)から11(bullseye)にしようとしてなんか言われた件

この辺りを読みつつ、「簡単だなぁ」とやってみたところ、ハマった点が1つ。 www.debian.org

/etc/apt/sources.list を書き換え

busterになっているのをbullseyeにする

deb http://ftp.jp.debian.org/debian buster main
deb-src http://ftp.jp.debian.org/debian buster main

deb http://security.debian.org/debian-security buster/updates main
deb-src http://security.debian.org/debian-security buster/updates main

deb http://ftp.jp.debian.org/debian bullseye main
deb-src http://ftp.jp.debian.org/debian bullseye main

deb http://security.debian.org/debian-security bullseye/updates main
deb-src http://security.debian.org/debian-security bullseye/updates main

って感じ。

で、エラー発生

このまま、# apt updateすると

E: リポジトリ http://security.debian.org/debian-security bullseye-updates Release には Release ファイルがありません。 って言われる。

んー、と思って色々調べたらbullseye-security とbullseye-updatesで分かれているみたい。

deb http://ftp.jp.debian.org/debian bullseye main contrib non-free
deb-src http://ftp.jp.debian.org/debian bullseye main contrib non-free

deb http://security.debian.org/debian-security bullseye-security main contrib non-free
deb-src http://security.debian.org/debian-security bullseye-security main contrib non-free

deb http://ftp.jp.debian.org/debian bullseye-updates main contrib non-free
deb-src http://ftp.jp.debian.org/debian bullseye-updates main contrib non-free

ってやると上手くいった。その後、apt update → apt upgrade → apt full-upgrade → apt dist-upgrade と。

MySQLが入ってなかった。

MariaDBを入れておく。

apt install -y mariadb-server

PHPからつながらないので、なんだろう?と思ったらMySQLモジュールが入ってない…

# apt install php7.4-mysql

Apache再起動。

WordPress5.5系にUpgradeするとDBアップデートでエラーが出てしまう件

結論から言うと、5.5は関係なかった。

WordPressのcron履歴の取り方が変だった。

There has been a critical error on your website.

Learn more about debugging in WordPress.

とか出た。日本語だと「このサイトで重大なエラーが発生しました」ってやつ。何の手掛かりも無いのでソースコードを追ってみた。

function upgrade_550() {

で引っかかってるっぽい。wp-admin/includes/upgrade.php

もっと追っていくと最後の

<?php
        wp_schedule_single_event( time() + ( 1 * MINUTE_IN_SECONDS ), 'wp_update_comment_type_batch' );

ところ。

function _set_cron_array( $cron ) {

wp-includes/cron.php
をさかのぼっていくと、

function wp_schedule_single_event( $timestamp, $hook, $args = array() ) {

function _set_cron_array( $cron ) {

return update_option( 'cron', $cron );

が詰まっているッぽい。 なんだよ、こんな基本的なもの…

この$cronなんなん?

と思って、ログに吐き出させると、

{i:1522275037;a:3:{s:16:"wp_version_check";a:1:{s:32:"40cd750bba9870f18aada2478b24840a";a:3:{s:8:"schedule";s:10:"twicedaily";s:4:"args";a:0:{}s:8:"interval";i:43200;}}s:17:"wp_update_plugins";a:1:{s:32:"40cd750bba9870f18aada2478b24840a";a:3:{s:8:"schedule";s:10:"twicedaily";s:4:"args";a:0:{}s:8:"interval";i:43200;}}s:16:"wp_update_themes";a:1:{s:32:"40cd750bba9870f18aada2478b24840a";a:3:{s:8:"schedule";s:10:"twicedaily";s:4:"args";a:0:{}s:8:"interval";i:43200;}}}i:1522297407;a:1:{s:19:"wp_scheduled_delete";a:1:{s:32:"40cd750bba9870f18aada2478b24840a";a:3:{s:8:"schedule";s:5:"daily";s:4:"args";a:0:{}s:8:"interval";i:86400;}}}i:1522300588;a:1:{s:30:"wp_scheduled_auto_draft_delete";a:1:{s:32:"40cd750bba9870f18aada2478b24840a";a:3:{s:8:"schedule";s:5:"daily";s:4:"args";a:0:{}s:8:"interval";i:86400;}}}i:1522308843;a:1:{s:25:"delete_expired_transients";a:1:{s:32:"40cd750bba9870f18aada2478b24840a";a:3:{s:8:"schedule";s:5:"daily";s:4:"args";a:0:{}s:8:"interval";i:86400;}}}i:1522826720;a:1:{s:8:"do_pings";a:1:{s:32:"40cd750bba9870f18aada2478b24840a";a:2:{s:8:"schedule";b:0;s:4:"args";a:0:{}}}}i:1527828937;a:1:{s:34:"wp_privacy_delete_old_export_files";a:1:{s:32:"40cd750bba9870f18aada2478b24840a";a:3:{

とかで軽く10Mを越える配列データが入ってた。やべぇやつや。

なんだよこのオプションは…

仕方ないのでDBを見てみる…

select * from wp_options where option_name="cron";
ERROR 2020 (HY000): Got packet bigger than 'max_allowed_packet'

Oh...

既にDBからしてやべぇやつやったのか…

なんかこれまでの運用でやらかしてしまったんかなー…とか思いつつこれ何かなと思うと、WordPressのcronのログっぽい。 いらんわー。消す。

なんか困ってる人もいたみたい。 そりゃ困るよな…

MySQLコマンドから

MariaDB > select option_id from wp_options where option_name="cron";
+-----------+
| option_id |
+-----------+
|        99 |
+-----------+
1 row in set (0.016 sec)

MariaDB > update wp_options set option_value="" where option_id=99;

で、またDB更新させると… できたー。

iPhoneの連絡先を編集すると「不正なパスワード cardDAVアカウント"Google“のパスワードを入力 」が出てしまう件の対応

こんなケースですね。

support.google.com

これは、iPhoneの設定の連絡先としてGoogleを追加しているのが原因です。 「設定」アプリから、連絡先>アカウントの部分
f:id:tohokuaiki:20210530174852p:plain

で、このパスワードがおかしいんかな?と思ってGoogleアカウントのいつものパスワードを入力しても全然ダメ…という流れで上記のエラーが出たりします。

f:id:tohokuaiki:20210530175047p:plain

実は、このパスワードは二段階認証が不要なパスワードを独自に発行する必要があります。
通常のログインだとiPhoneの連絡先を編集するたびに二段階認証が来て面倒なためです。

アプリパスワードの作成

Googleアカウントのセキュリティのページに移動します。

Googleへのログイン」の見出しに「アプリパスワード」という項目があるのでクリックします。
f:id:tohokuaiki:20210530175356p:plain

用途があるので、連絡先>iPhoneと選択して「生成」します。
f:id:tohokuaiki:20210530175952p:plain

パスワードが出てくるので、このパスワードを使ってGoogleにログインします。

ちなみに、このパスワードはこれ専用なので削除が可能です。↑のパスワードも削除済みです。

Gulp4で画像のCacheBusterみたいな

今更だけど。

ファイル構造はこんな感じ。

gulpfile.js
gulp-functions.js
package.json
package-lock.json
htdocs/
├── css
│   └── images
├── images
└── js
src
├── javascripts
│   └── vendor
└── scss

src/scss => htdocs/css
src/javascripts => htdocs/js
へとコンパイルされる。

package.json

{
  "name": "bt-gulpfile",
  "version": "1.0.0",
  "description": "",
  "main": "gulpfile.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "private": true,
  "devDependencies": {
    "cheerio": "^1.0.0-rc.3",
    "compass-mixins": "^0.12.10",
    "event-stream": "^4.0.1",
    "gulp": "^4.0.2",
    "gulp-autoprefixer": "^6.1.0",
    "gulp-concat": "^2.6.1",
    "gulp-connect": "^5.7.0",
    "gulp-ejs": "^4.1.1",
    "gulp-htmlmin": "^5.0.1",
    "gulp-if": "^2.0.2",
    "gulp-plumber": "^1.2.1",
    "gulp-postcss": "^8.0.0",
    "gulp-sass": "^4.0.2",
    "gulp-sourcemaps": "^2.6.5",
    "gulp-uglify": "^3.0.2",
    "minimist": "^1.2.0",
    "postcss-cachebuster": "^0.1.6"
  },
  "dependencies": {},
  "browserslist": [
    "last 1 version",
    "ie 9",
    "Android 4.2"
  ]
}

gulpfile.js

var gulp        = require('gulp');
var gulpF       = require('./gulp-functions.js');
var connect     = require('gulp-connect');
var ejs         = require('gulp-ejs');
var uglify      = require("gulp-uglify");
var plumber     = require("gulp-plumber");
var concat      = require("gulp-concat");
var sourcemaps  = require("gulp-sourcemaps");
var autoprefixer= require('gulp-autoprefixer');
var sass        = require('gulp-sass');
var htmlmin     = require('gulp-htmlmin');
var minimist    = require("minimist");
var gulpif      = require('gulp-if');
var postcss     = require('gulp-postcss');
var cachebuster = require('postcss-cachebuster');
const fs        = require('fs');

var options = minimist(process.argv.slice(2), {
  string: 'env',
  default: { env: 'production' }
});

const config = {
    production: options.env !== 'dev',
    sourceMaps: options.env === 'dev'
};

var packageJson = JSON.parse(fs.readFileSync('./package.json'));

gulp.task('javascript', function(cb){
    gulp.src(['./src/javascripts/vendor/*.js', './src/javascripts/*.js'])
      .pipe(plumber())
        .pipe(gulpif(config.sourceMaps, sourcemaps.init()))
          .pipe(concat('dest.js'))
            .pipe(uglify())
              .pipe(gulpif(config.sourceMaps, sourcemaps.write()))
                .pipe(gulp.dest("htdocs/js/"));
    cb();
});

gulp.task('sass', function (cb) {
    gulp.src(['./src/scss/*.scss', './src/scss/vendor/*.scss'])
      .pipe(plumber())
        .pipe(gulpif(config.sourceMaps, sourcemaps.init()))
          .pipe(concat('style.css'))
            .pipe(sass({
              outputStyle: 'compressed',
            }))
              .pipe(autoprefixer({
                cascade: false
              }))
                .pipe(postcss([
                    cachebuster({
                      type: 'checksum',
                      cssPath : '/htdocs/css',
                    }),
                    ]))
                  .pipe(gulpif(config.sourceMaps, sourcemaps.write()))
                    .pipe(gulp.dest('./htdocs/css/'));
    cb();
});

gulp.task('html', function (cb) {
    gulp.src(['./src/**/*.html', '!./src/**/_*.html'])
      .pipe(plumber())
        .pipe(ejs())
          .pipe(gulpF.htmlrev())
            .pipe(htmlmin({
              collapseWhitespace : false,
              removeComments : true
            }))
              .pipe(gulp.dest('./htdocs'));
    cb();
});

gulp.task('watch', function(cb){
    // auto compile
    gulp.watch(
        ['./src/**/*.html',
         './src/javascripts/**/*.js',
         './src/scss/**/*.scss',
         ],
        gulp.series('html', 'javascript', 'sass'));
    
    // browser live reload
    gulp.watch('htdocs').on('change', function(filepath){
        gulp.src(filepath, { read: false }).pipe(connect.reload());
    });
    cb();
});

gulp.task('connect', function(done) {
  connect.server({
    root: 'htdocs',
    livereload: true
  }, function () { this.server.on('close', done) });
});

// only-compile
gulp.task('compile', gulp.series(
    'html', 'javascript', 'sass'));

// dev
gulp.task('default', gulp.series(
    'html', 'javascript', 'sass',
    'watch', 'connect'));

gulp-functions.js

var eventstream = require('event-stream');
const cheerio   = require('cheerio');
const path      = require('path');
const fs        = require('fs');
const crypto    = require('crypto');

module.exports = {
    // htmlファイルのimg/script/linkのファイルにmd5値でリビジョンを付ける
    htmlrev : ()=>{
        return eventstream.map(function(file, cb) {
            var $ = cheerio.load(file.contents.toString(),
                                 { decodeEntities: false });
            $('link[rel="stylesheet"],script[src],img').each(function(i, ele) {
                let attrname = "";
                let hash;
                switch (ele.name.toLowerCase()){
                  case 'link':
                    attrname = 'href';
                    break;
                  case 'img':
                  case 'script':
                    attrname = 'src';
                    break;
                }
                if (attrname) {
                    let src = $(ele).attr(attrname);
                    if (!src.match(new RegExp('^https?:\/\/'))) {
                        var normPath = path.normalize(src);
                        if (normPath.indexOf(path.sep) === 0) {
                            dependencyPath = path.join(file.base, normPath);
                        }
                        else {
                            dependencyPath = path.resolve(path.dirname(file.path), normPath);
                        }
                        let hasQS = dependencyPath.lastIndexOf("?");
                        if (hasQS >= 0){
                            dependencyPath = dependencyPath.substring(0, hasQS);
                        }
                        // replace src => htdocs
                        dependencyPath = dependencyPath.replace(
                            path.sep+'src'+path.sep,
                            path.sep+'htdocs'+path.sep);
                        try {
                            data = fs.readFileSync(dependencyPath);
                            hash = crypto.createHash('md5');
                            hash.update(data.toString(), 'utf8');
                            $(ele).attr(attrname,
                                        $(ele).attr(attrname) + '?rev=' + hash.digest('hex'));
                        }
                        catch(e) {
                            // fail silently.
                            console.log('[ERROR] revision attach failed: ' + e.message);
                        }
                    }
                }
            });
            file.contents =
              new Buffer.from($.root().html());
            cb(null, file);
        });
    }
}

実行する

npxで実行

$ npx gulp watch

でファイル監視の開発モード

$ npx gulp compile

コンパイルしてくれる。

PHPでCSVファイルを書き出す時にBOMを付けて改行コードをCRLFにする

Streamってあんまり使わない…

こんな感じ。

<?php
use App\Helpers\CrlfFilter;
# Streamを開く
$stream = fopen('php://output', 'w');
 # BOMを付ける
fputs($stream, pack('C*',0xEF,0xBB,0xBF));
# 改行コード変換フィルターを登録
stream_filter_register('CrlfFilter', CrlfFilter::class); 
# 登録したフィルターを適用
stream_filter_prepend($stream,'CrlfFilter', STREAM_FILTER_WRITE);
#データ書き込み
fputcsv($stream, [1,2,3];
# 終了
fclose($stream);

stream_filterって初めて使った。

ちなみにCrlfFilterは

こちらからコピペした

<?php
namespace App\Helpers;

/**
 * Class CrlfFilter
 * @package App\Helpers
 * @brief 改行コードをLFからCRLFにする
 */
class CrlfFilter extends \php_user_filter
{
    public function filter($in, $out, &$consumed, $closing) {
        while ($bucket = stream_bucket_make_writeable($in)) {
            $bucket->data = preg_replace("/\n$/", '', $bucket->data);
            $bucket->data = preg_replace("/\r$/", '', $bucket->data);
            $bucket->data = $bucket->data . "\r\n";
            $consumed += $bucket->datalen;
            stream_bucket_append($out, $bucket);
        }
        return PSFS_PASS_ON;
    }
}