Latest Learning

Stuff I learned recently.

是因为 beads 会自动启用一个 daemon 进程,只要停掉即可 bd daemon --stop-all

git clonegit fetch 的时候,git 会在本地创建一些”快照”,记录远程仓库的最新状态,但这些不是真正的分支,当远端的 branch 删除之后,本地还会留着。

解决方法 1

git fetch --prune

解决方法 2

直接配置全局的 gitconfig 即可:

git config --global fetch.prune true

获取远端分支

Fetch the latest changes from the default remote upstream repository (if set)

git fetch

查看所有分支

List all branches (local and remote; the current branch is highlighted by *):

git branch --all

删除远端分支

git push origin --delete <branch_name>

删除本地分支

git branch -d <branch_name>

direnv 配合 rbw 平常用起来很好用,还不用担心密钥硬编码到代码中泄露。

export TF_VAR_ntfy_token=$(rbw get ntfy_github_action_token)
export TF_VAR_gmail_token=$(rbw get gmail_github_action_token)

⚠️ 重要注意

我在 macos 下配合 rbw 会有很大延迟 (解锁之后使用 rbw get 有时候会有接近 10s 的延迟),我研究发现是因为 rbw 的获取 ttyname() 有性能问题。

请在 .zshrc 中添加以下环境变量来解决这个问题:

export RBW_TTY=$(tty)

配置后响应时间可缩短到 1 秒内。

相关:修复 macos 下使用 rbw 获取密钥卡顿]

一个 curl 请求的例子:

curl -X POST 'https://api.github.com/repos/zhaochunqi/til-pages/dispatches' \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/vnd.github.v3+json' \
  --header 'Authorization: Bearer github_pat_xxxxx' \
  --header 'X-GitHub-Api-Version: 2022-11-28' \
  --data '{"event_type": "til-updated"}'

其中 github_pat_xxxxx 是 github 的 token,可以在 github token 页面生成。使用 fine-grained token, token 权限需要有 Contents: Read and write.

til-updated 是 github action 的事件类型,可以在 github action 的配置文件中定义。

on:
  repository_dispatch:
    types: [til-updated]

这样就可以通过 curl 请求触发 github action 了。

使用命令 source_env 可以在当前目录的 .envrc 文件中引用其他目录的 .envrc 文件。

使用具体情况可参考:

目录如下:
 tree -al A
drwxr-xr-x@  - zhaochunqi 14 Dec 15:02 A
.rw-r--r--@ 56 zhaochunqi 14 Dec 15:02 ├── .envrc
drwxr-xr-x@  - zhaochunqi 14 Dec 15:02 └── B
.rw-r--r--@ 57 zhaochunqi 14 Dec 15:02     └── .envrc

A/.envrc 文件内容如下:

export A="this is A"
export B="this is B from A folder"

A/B/.enrc 文件内容如下:

source_env ../.envrc
export B="This is B from B folder"

在 A 目录下执行 direnv allow 后,进入 B 目录,执行 direnv allow 后,可以看到如下输出:

Desktop/A/B 
 echo $A
this is A

Desktop/A/B 
 echo $B
This is B from B folder

可以看到,在 B 目录下,可以访问到 A 目录下的环境变量,并且 B 目录下的环境变量覆盖了 A 目录下的环境变量。这正是我们想要的。

使用 https://github.com/marketplace/actions/create-pull-request 的时候遇到无法创建 pr 的问题,需要做如下配置:

1. 检查仓库设置

请访问:如:https://github.com/zhaochunqi/dns/settings/actions (替换成你自己的) 找到 “Workflow permissions” 部分,确保:

  • ✅ 选择 “Read and write permissions”
  • ✅ 勾选 “Allow GitHub Actions to create and approve pull requests”

2. workflow 中添加

permissions:
  contents: write
  pull-requests: write
  1. 需要一个 Discord 的 Webhook 地址,可以通过在 Discord 的频道中创建一个 Webhook 来获取。如:https://discord.com/api/webhooks/1111111111111/xxxxxxxxxxxxxxxxxxxxxxxxxxxx
  2. 需要安装 webhook 插件,在 Jellyfin 的插件商店中搜索 webhook 即可找到。

配置方法:

勾选你要的 play 的一些事件,然后填写你的 Webhook 地址,然后在 template 处添加:

{
    "content": "{{MentionType}}",
    "avatar_url": "{{ServerUrl}}/Users/{{UserId}}/Images/Primary",
    "username": "{{NotificationUsername}}",
    "embeds": [
        {
            "author": {
                {{#if_equals ItemType 'Episode'}}
                    "name": "Playback Started • {{{SeriesName}}} S{{SeasonNumber00}}E{{EpisodeNumber00}} ~ {{{Name}}}",
                {{else}}
                    "name": "Playback Started • {{{Name}}} ({{Year}})",
                {{/if_equals}}

                "url": "{{ServerUrl}}/web/#/details?id={{ItemId}}&serverId={{ServerId}}"
            },
            
            "thumbnail":{
                "url": "{{ServerUrl}}/Items/{{ItemId}}/Images/Primary"
            },

            "description": "> {{{Overview}}}\n\n``[{{PlaybackPosition}}/{{RunTime}}]``",

            "color": "3394611",

            "footer": {
                "text": "{{{ServerName}}}",
                "icon_url": "{{AvatarUrl}}"
            },

            "timestamp": "{{Timestamp}}"
        }
    ]
}

随便播放一个视频,你可以看到 Discord 频道中有消息了。如果没有成功,记得到 Jellyfin 的日志中查看错误信息。(不要随便勾选一些事件或者忽略 template 之类的选项)

Traefik 底层使用的是 lego 库来处理 ACME (Let’s Encrypt) 协议。在标准的 DNS-01 验证流程中,Traefik 会严格遵循 “创建 -> 验证 -> 清理” 的生命周期。所以如果有 _acme-challenge 之类的 dns 记录存在,除非正在签发过程中,否则是可以删除掉的。

签发流程:LIVE EDITOR

flowchart TD
    Start["开始 DNS-01 挑战"]
    
    Start --> GetInfo["获取挑战信息<br/>GetChallengeInfo()"]
    GetInfo --> CNAMECheck{"检查 CNAME"}
    
    CNAMECheck -->|有 CNAME| FollowCNAME["跟随 CNAME 链<br/>getChallengeFQDN()"]
    CNAMECheck -->|无 CNAME| UseFQDN["使用原始 FQDN"]
    
    FollowCNAME --> EffectiveFQDN["获得 EffectiveFQDN"]
    UseFQDN --> EffectiveFQDN
    
    EffectiveFQDN --> Present["创建 TXT 记录<br/>Present()"]
    
    Present --> ProviderCheck{"DNS 提供商类型"}
    
    ProviderCheck -->|标准提供商| StandardProvider["标准 DNS 提供商<br/>直接创建 TXT 记录"]
    ProviderCheck -->|ACME-DNS| ACMEDNSProvider["ACME-DNS 提供商"]
    
    ACMEDNSProvider --> AccountCheck{"检查账户"}
    AccountCheck -->|账户存在| UpdateTXT["更新 TXT 记录"]
    AccountCheck -->|账户不存在| CreateAccount["创建新账户"]
    
    CreateAccount --> CNAMERequired["返回 ErrCNAMERequired<br/>需要手动创建 CNAME"]
    CNAMERequired --> ManualCNAME["手动创建 CNAME 记录"]
    ManualCNAME --> UpdateTXT
    
    StandardProvider --> Propagation["等待 DNS 传播"]
    UpdateTXT --> Propagation
    
    Propagation --> Polling["轮询检查<br/>wait.For()"]
    Polling --> Validate{"验证成功?"}
    
    Validate -->|是| CertIssued["证书签发成功"]
    Validate -->|否| Timeout{"超时?"}
    
    Timeout -->|是| Error["签发失败"]
    Timeout -->|否| Polling
    
    CertIssued --> Cleanup["清理 DNS 记录<br/>CleanUp()"]
    Error --> Cleanup
    
    Cleanup --> DeleteTXT["删除 TXT 记录"]
    DeleteTXT --> End["流程结束"]
    
    %% 样式
    classDef default fill:#f9f9f9,stroke:#333,stroke-width:2px
    classDef process fill:#e1f5fe,stroke:#01579b,stroke-width:2px
    classDef decision fill:#fff3e0,stroke:#e65100,stroke-width:2px
    classDef error fill:#ffebee,stroke:#c62828,stroke-width:2px
    classDef success fill:#e8f5e8,stroke:#2e7d32,stroke-width:2px
    
    class GetInfo,FollowCNAME,UseFQDN,EffectiveFQDN,Present,StandardProvider,UpdateTXT,Propagation,Polling,Cleanup,DeleteTXT process
    class CNAMECheck,ProviderCheck,AccountCheck,Validate,Timeout decision
    class CNAMERequired,ManualCNAME,Error error
    class CertIssued,End success

nixos 更新 flake

flakes 的设计初衷是“重现性”(Reproducibility),而不是“实时性”。

获取最新的分支构建信息

# 只更新 nixpkgs 这个 input
nix flake update nixpkgs

# 或者更新所有 inputs
nix flake update

注意,建议更新所有 inputs,不然可能会出现 nixpkgs 和 home-manager 版本不一致的问题

当我在 cronjob 中设置:LOGSEQ_FOLDER=$HOME/logseq 时 (这个语法会被 cronjob 设置为环境变量),在后续的命令调用 LOGSEQ_FOLDER 这个变量的时候,并不能正确的获取到 $HOME 变量。但是我们可以尝试在 SHELL 中运行命令export LOGSEQ_FOLDER=$HOME/logseq,然后从环境变量中查找 env|grep LOGSEQ_FOLDER 得到结果是:/home/alex/logseq, 为什么?

因为 cronjob 中不会对环境变量的值 $HOME 二次展开,但是 shell 中,是直接展开了的!

  • cronjob 中,LOGSEQ_FOLDER=$HOME/logseq 表示的是一个键值对,原样放入子进程的模块的环境变量中。
  • shell 中,LOGSEQ_FOLDER=$HOME/logseq 表示的是一个表达式,会先计算,再展开,所以你 export 后的环境变量就已经是展开过后的了。

在 windows 下的手机连接有时候会报错:“Microsoft 手机连接 - 无法连接到你的 Android 设备,因为你正在尝试访问中国以外的应用程序,目前我们不支持漫游区域。”

只需要添加对应的 “dcg.microsoft.com” 为直连即可。

Bitwarden 为例:

确定软件已安装

ls -la /Applications/ | grep -i bitwarden

使用 SPCTL 查看该应用的信息

spctl -a -t exec -vvv /Applications/Bitwarden.app

输出如下:

/Applications/Bitwarden.app: accepted
source=Notarized Developer ID
origin=Developer ID Application: 8bit Solutions LLC (LTZ2PFU5D6)

如果 source 是 Mac App Store, 那就是通过 app store 下载的,反之如果 source 是 Notarized Developer ID, 那就是通过 DMG 下载的。

这个命令的详细解析

这个命令是 macOS 系统中用于 Gatekeeper(门禁)安全机制的 spctl 工具的一个用法。具体解释如下:

spctl -a -t exec -vvv /Applications/Bitwarden.app

各部分含义:

  • spctl
    是 macOS 自带的命令行工具,全称是 System Policy Control,用于管理系统策略,尤其是 Gatekeeper 对应用程序的验证和授权。

  • -a(或 --assess
    表示“评估”(assess)指定的项目,即检查该应用是否被系统策略允许运行。

  • -t exec(或 --type execute
    指定评估类型为“可执行”(executable),即检查该应用是否可以被当作可执行程序运行。这是针对应用程序(.app)的标准类型。

  • -vvv
    表示输出详细(verbose)信息,v 越多,输出越详细。-vvv 是非常详细的输出,会显示签名信息、证书链、权限来源等。

  • /Applications/Bitwarden.app
    要评估的目标应用程序路径,这里是安装在应用程序文件夹中的 Bitwarden 密码管理器。


这个命令的作用:

检查 Bitwarden.app 是否通过了 macOS 的 Gatekeeper 验证,是否被允许运行。

运行后,你会看到类似这样的输出(取决于实际情况):

  • 如果应用已正确签名且被信任:
    /Applications/Bitwarden.app: accepted
    source=Apple Notarized Developer ID
    origin=Developer ID Application: Bitwarden Inc. (XXXXX)

/etc/nixos/configuration 中这样配置即可:

services.interception-tools =
  let
    itools = pkgs.interception-tools;
    itools-caps = pkgs.interception-tools-plugins.caps2esc;
  in
  {
    enable = true;
    plugins = [ itools-caps ];
    # requires explicit paths: https://github.com/NixOS/nixpkgs/issues/126681
    udevmonConfig = pkgs.lib.mkDefault ''
      - JOB: "${itools}/bin/intercept -g $DEVNODE | ${itools-caps}/bin/caps2esc -m 1 | ${itools}/bin/uinput -d $DEVNODE"
        DEVICE:
          EVENTS:
            EV_KEY: [KEY_CAPSLOCK, KEY_ESC]
    '';
  };

参考连接:https://discourse.nixos.org/t/best-way-to-remap-caps-lock-to-esc-with-wayland/39707/6