在之前的文章中(点击此处查看上一篇文章),我们了解了如何识别包含密钥的文件,将密钥添加到 .gitignore ,通过 git-secret 进行加密,以及将加密文件提交到存储库。在本篇文章中,将带你了解如何在 Docker 容器中设置 git-secretgpg,通过 Makefile recipe 为不同的场景创建工作流。

Makefile Adjustment

git-secretgpg 指令添加到 Makefile 中 .make/01-00-application-setup.mk

# File: .make/01-00-application-setup.mk

#...

# gpg

DEFAULT_SECRET_GPG_KEY?=secret.gpg
DEFAULT_PUBLIC_GPG_KEYS?=.dev/gpg-keys/* .PHONY: gpg
gpg: ## Run gpg commands. Specify the command e.g. via ARGS="--list-keys"
$(EXECUTE_IN_APPLICATION_CONTAINER) gpg $(ARGS) .PHONY: gpg-export-public-key
gpg-export-public-key: ## Export a gpg public key e.g. via EMAIL="john.doe@example.com" PATH=".dev/gpg-keys/john-public.gpg"
@$(if $(PATH),,$(error PATH is undefined))
@$(if $(EMAIL),,$(error EMAIL is undefined))
"$(MAKE)" -s gpg ARGS="gpg --armor --export $(EMAIL) > $(PATH)" .PHONY: gpg-export-private-key
gpg-export-private-key: ## Export a gpg private key e.g. via EMAIL="john.doe@example.com" PATH="secret.gpg"
@$(if $(PATH),,$(error PATH is undefined))
@$(if $(EMAIL),,$(error EMAIL is undefined))
"$(MAKE)" -s gpg ARGS="--output $(PATH) --armor --export-secret-key $(EMAIL)" .PHONY: gpg-import
gpg-import: ## Import a gpg key file e.g. via GPG_KEY_FILES="/path/to/file /path/to/file2"
@$(if $(GPG_KEY_FILES),,$(error GPG_KEY_FILES is undefined))
"$(MAKE)" -s gpg ARGS="--import --batch --yes --pinentry-mode loopback $(GPG_KEY_FILES)" .PHONY: gpg-import-default-secret-key
gpg-import-default-secret-key: ## Import the default secret key
"$(MAKE)" -s gpg-import GPG_KEY_FILES="$(DEFAULT_SECRET_GPG_KEY)" .PHONY: gpg-import-default-public-keys
gpg-import-default-public-keys: ## Import the default public keys
"$(MAKE)" -s gpg-import GPG_KEY_FILES="$(DEFAULT_PUBLIC_GPG_KEYS)" .PHONY: gpg-init
gpg-init: gpg-import-default-secret-key gpg-import-default-public-keys ## Initialize gpg in the container, i.e. import all public and private keys # git-secret .PHONY: git-secret
git-secret: ## Run git-secret commands. Specify the command e.g. via ARGS="hide"
$(EXECUTE_IN_APPLICATION_CONTAINER) git-secret $(ARGS) .PHONY: secret-init
secret-init: ## Initialize git-secret in the repository via `git-secret init`
"$(MAKE)" -s git-secret ARGS="init" .PHONY: secret-init-gpg-socket-config
secret-init-gpg-socket-config: ## Initialize the config files to change the gpg socket locations
echo "%Assuan%" > .gitsecret/keys/S.gpg-agent
echo "socket=/tmp/S.gpg-agent" >> .gitsecret/keys/S.gpg-agent
echo "%Assuan%" > .gitsecret/keys/S.gpg-agent.ssh
echo "socket=/tmp/S.gpg-agent.ssh" >> .gitsecret/keys/S.gpg-agent.ssh
echo "extra-socket /tmp/S.gpg-agent.extra" > .gitsecret/keys/gpg-agent.conf
echo "browser-socket /tmp/S.gpg-agent.browser" >> .gitsecret/keys/gpg-agent.conf .PHONY: secret-encrypt
secret-encrypt: ## Decrypt secret files via `git-secret hide`
"$(MAKE)" -s git-secret ARGS="hide" .PHONY: secret-decrypt
secret-decrypt: ## Decrypt secret files via `git-secret reveal -f`
"$(MAKE)" -s git-secret ARGS="reveal -f" .PHONY: secret-decrypt-with-password
secret-decrypt-with-password: ## Decrypt secret files using a password for gpg via `git-secret reveal -f -p $(GPG_PASSWORD)`
@$(if $(GPG_PASSWORD),,$(error GPG_PASSWORD is undefined))
"$(MAKE)" -s git-secret ARGS="reveal -f -p $(GPG_PASSWORD)" .PHONY: secret-add
secret-add: ## Add a file to git secret via `git-secret add $FILE`
@$(if $(FILE),,$(error FILE is undefined))
"$(MAKE)" -s git-secret ARGS="add $(FILE)" .PHONY: secret-cat
secret-cat: ## Show the contents of file to git secret via `git-secret cat $FILE`
@$(if $(FILE),,$(error FILE is undefined))
"$(MAKE)" -s git-secret ARGS="cat $(FILE)" .PHONY: secret-list
secret-list: ## List all files added to git secret `git-secret list`
"$(MAKE)" -s git-secret ARGS="list" .PHONY: secret-remove
secret-remove: ## Remove a file from git secret via `git-secret remove $FILE`
@$(if $(FILE),,$(error FILE is undefined))
"$(MAKE)" -s git-secret ARGS="remove $(FILE)" .PHONY: secret-add-user
secret-add-user: ## Remove a user from git secret via `git-secret tell $EMAIL`
@$(if $(EMAIL),,$(error EMAIL is undefined))
"$(MAKE)" -s git-secret ARGS="tell $(EMAIL)" .PHONY: secret-show-users
secret-show-users: ## Show all users that have access to git secret via `git-secret whoknows`
"$(MAKE)" -s git-secret ARGS="whoknows" .PHONY: secret-remove-user
secret-remove-user: ## Remove a user from git secret via `git-secret killperson $EMAIL`
@$(if $(EMAIL),,$(error EMAIL is undefined))
"$(MAKE)" -s git-secret ARGS="killperson $(EMAIL)" .PHONY: secret-diff
secret-diff: ## Show the diff between the content of encrypted and decrypted files via `git-secret changes`
"$(MAKE)" -s git-secret ARGS="changes"

工作流程

使用 git-secret 非常简单:

  • 初始化 git-secret
  • 添加所有用户。
  • 添加所有机密文件并确保这些文件通过 .gitignore 被忽略。
  • 加密文件。
  • 如果团队其他成员对文件进行更改,则需要解密文件→更新文件→再次提交加密文件
  • 如果对解密的文件进行更改,修改完需要再次重新进行加密。

下面的“流程挑战”部分展示了一些在可能遇到的问题,“场景”部分将会展示一些常见场景的具体示例。

流程中的挑战

从流程的角度,一起来看看在过程中可能遇到的一些困难和挑战,以及如何处理解决。

更新机密

更新机密时,请确保先解密文件,从而避免使用可能仍存在本地的旧文件。可以通过检查最新的 main 分支并运行 git secret reveal,来获得最新版本的机密文件。也可以使用 post-merge Git hook 自动执行此操作,不过要注意覆盖本地机密文件的风险哦。

代码审查和合并冲突

由于无法对加密文件进行很好的区分,因此当涉及机密时代码审查变得更加困难。这是可以尝试使用 GitLab 进行审查,首先检查 .gitsecret/paths/mapping.cfg 文件的差异,在 UI 中查看哪些文件已更改。

此外,可以根据以下步骤来查看:

  • 检查 main 分支。

  • 通过 git secret reveal -f 解密文件

  • 查看 feature-branch.

  • 运行 git secret changes 来查看 main 的解密文件和feature-branch 中加密文件之间的差异。

当多个团队成员需要同时修改不同分支上的机密文件时,情况会更加复杂一些,因为Git 无法智能处理增量更新。

本地 git-secret 和 gpg 设置

当团队的所有人员将 git-secret 安装在本地,并且使用他们自己的 gpg 密钥,这也意味着团队的成本会随之增加,原因如下:

  • 新加入开发团队的人员需要:

    • 本地安装 git-secret(*)
    • 在本地安装和设置 gpg(*)
    • 创建 gpg 密钥对
  • 必须由所有其他团队成员 (*) 添加公钥。
  • 必须通过添加密钥的用户 git secret tell
  • 机密需要重新加密。

对于离开团队的人员:

  • 所有其他团队成员(*) 都需要删除公钥。
  • 通过 git secret killperson 删除密钥的用户。
  • 机密需要重新加密。

另外,需要确保 git-secretgpg 版本保持最新,避免遇到任何兼容性问题。作为替代方案,也可以通过 Docker 处理,而上述步骤中标注(*) 则可以省去,也就是不需要设置本地的 git-secretgpg

为了更加便捷,将存储库中每个开发人员的公共 gpg 密钥放在 .dev/gpg-keys/,而私钥命名为 secret.gpg 并放在代码库的根目录中。

在此设置中,secret.gpg 还必须被添加到 .gitignore 文件中。

# File: .gitignore
#...
vendor/
secret.gpg

然后可以使用 make 目标简化导入:

# gpg

DEFAULT_SECRET_GPG_KEY?=secret.gpg
DEFAULT_PUBLIC_GPG_KEYS?=.dev/gpg-keys/* .PHONY: gpg
gpg: ## Run gpg commands. Specify the command e.g. via ARGS="--list-keys"
$(EXECUTE_IN_APPLICATION_CONTAINER) gpg $(ARGS) .PHONY: gpg-import
gpg-import: ## Import a gpg key file e.g. via GPG_KEY_FILES="/path/to/file /path/to/file2"
@$(if $(GPG_KEY_FILES),,$(error GPG_KEY_FILES is undefined))
"$(MAKE)" -s gpg ARGS="--import --batch --yes --pinentry-mode loopback $(GPG_KEY_FILES)" .PHONY: gpg-import-default-secret-key
gpg-import-default-secret-key: ## Import the default secret key
"$(MAKE)" -s gpg-import GPG_KEY_FILES="$(DEFAULT_SECRET_GPG_KEY)" .PHONY: gpg-import-default-public-keys
gpg-import-default-public-keys: ## Import the default public keys
"$(MAKE)" -s gpg-import GPG_KEY_FILES="$(DEFAULT_PUBLIC_GPG_KEYS)" .PHONY: gpg-init
gpg-init: gpg-import-default-secret-key gpg-import-default-public-keys ## Initialize gpg in the container, i.e. import all public and private keys

上述操作需要在容器启动后运行一次。

场景

先假设以下这些条件:

  • 已检查过 Git 存储库。git checkout part-6-git-secret-encrypt-repository-docker

  • 没有正在运行的 Docker 容器。make docker-down

  • 已删除现有 git-secret 文件夹、中的密钥.dev/gpg-keyssecret.gpg密钥和 passwords.*文件。rm -rf .gitsecret/ .dev/gpg-keys/* secret.gpg passwords.*

gpg 密钥的初始设置

不幸的是,我没有找到通过makedocker创建和导出gpg密钥的方法。你需要交互式地运行这些命令,或者传递一个带换行的字符串给它。这两件事在makedocker中都复杂得可怕。因此,你需要登录到应用程序的容器中,并在那里直接运行这些命令。这不是很简单,但无论如何,这只需要在一个新的开发人员入职时做一次。

密钥导出到 secret.gpg,公钥导出到 gp.dev/gpg-keys/alice-public.gpg

# start the docker setup
make docker-up # log into the container ('winpty' is only required on Windows)
winpty docker exec -ti dofroscra_local-application-1 bash # export key pair
name="Alice Doe"
email="alice@example.com"
gpg --batch --gen-key < .dev/gpg-keys/alice-public.gpg
$ make docker-up
ENV=local TAG=latest DOCKER_REGISTRY=docker.io DOCKER_NAMESPACE=dofroscra APP_USER_NAME=application APP_GROUP_NAME=application docker compose -p dofroscra_local --env-file ./.docker/.env -f ./.docker/docker-compose/docker-compose.yml -f ./.docker/docker-compose/docker-compose.local.yml up -d
Container dofroscra_local-application-1 Created
...
Container dofroscra_local-application-1 Started
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
...
95f740607586 dofroscra/application-local:latest "/usr/sbin/sshd -D" 21 minutes ago Up 21 minutes 0.0.0.0:2222->22/tcp dofroscra_local-application-1 $ winpty docker exec -ti dofroscra_local-application-1 bash
root:/var/www/app# name="Alice Doe"
root:/var/www/app# email="alice@example.com"
gpg --batch --gen-key < Key-Type: 1
> Key-Length: 2048
> Subkey-Type: 1
> Subkey-Length: 2048
> Name-Real: $name
> Name-Email: $email
> Expire-Date: 0
> %no-protection
> EOF
gpg: directory '/root/.gnupg' created
gpg: keybox '/root/.gnupg/pubring.kbx' created
gpg: /root/.gnupg/trustdb.gpg: trustdb created
gpg: key BBBE654440E720C1 marked as ultimately trusted
gpg: directory '/root/.gnupg/openpgp-revocs.d' created
gpg: revocation certificate stored as '/root/.gnupg/openpgp-revocs.d/225C736E0E70AC222C072B70BBBE654440E720C1.rev' root:/var/www/app# gpg --output secret.gpg --armor --export-secret-key $email
root:/var/www/app# head secret.gpg
-----BEGIN PGP PRIVATE KEY BLOCK----- lQOYBGJD+bwBCADBGKySV5PINc5MmQB3PNvCG7Oa1VMBO8XJdivIOSw7ykv55PRP
3g3R+ERd1Ss5gd5KAxLc1tt6PHGSPTypUJjCng2plwD8Jy5A/cC6o2x8yubOslLa
x1EC9fpcxUYUNXZavtEr+ylOaTaRz6qwSabsAgkg2NZ0ey/QKmFOZvhL8NlK9lTI
GgZPTiqPCsr7hiNg0WRbT5h8nTmfpl/DdTgwfPsDn5Hn0TEMa79WsrPnnq16jsq0
Uusuw3tOmdSdYnT8j7m1cpgcSj0hRF1eh4GVE0o62GqeLTWW9mfpcuv7n6mWaCB8
DCH6H238gwUriq/aboegcuBktlvSY21q/MIXABEBAAEAB/wK/M2buX+vavRgDRgR
hjUrsJTXO3VGLYcIetYXRhLmHLxBriKtcBa8OxLKKL5AFEuNourOBdcmTPiEwuxH
5s39IQOTrK6B1UmUqXvFLasXghorv8o8KGRL4ABM4Bgn6o+KBAVLVIwvVIhQ4rlf root:/var/www/app# gpg --armor --export $email > .dev/gpg-keys/alice-public.gpg
root:/var/www/app# head .dev/gpg-keys/alice-public.gpg
-----BEGIN PGP PUBLIC KEY BLOCK----- mQENBGJD+bwBCADBGKySV5PINc5MmQB3PNvCG7Oa1VMBO8XJdivIOSw7ykv55PRP
3g3R+ERd1Ss5gd5KAxLc1tt6PHGSPTypUJjCng2plwD8Jy5A/cC6o2x8yubOslLa
x1EC9fpcxUYUNXZavtEr+ylOaTaRz6qwSabsAgkg2NZ0ey/QKmFOZvhL8NlK9lTI
GgZPTiqPCsr7hiNg0WRbT5h8nTmfpl/DdTgwfPsDn5Hn0TEMa79WsrPnnq16jsq0
Uusuw3tOmdSdYnT8j7m1cpgcSj0hRF1eh4GVE0o62GqeLTWW9mfpcuv7n6mWaCB8
DCH6H238gwUriq/aboegcuBktlvSY21q/MIXABEBAAG0HUFsaWNlIERvZSA8YWxp
Y2VAZXhhbXBsZS5jb20+iQFOBBMBCgA4FiEEIlxzbg5wrCIsBytwu75lREDnIMEF
AmJD+bwCGy8FCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQu75lREDnIMEN4Af+

至此 alice@example.com 就有了一个新机密和私钥,将其导出到 secret.gpg.dev/gpg-keys/alice-public.gpg。剩下的命令现在可以直接在application 容器外的主机上运行。

git-secret 的初始设置

现在来将 git-secret 引入一个新的代码库,然后运行以下命令。

初始化 git-secret

make secret-init$ make secret-init
"C:/Program Files/Git/mingw64/bin/make" -s git-secret ARGS="init";
git-secret: init created: '/var/www/app/.gitsecret/'

应用 gpg 对共享目录进行修复:

$ make secret-init-gpg-socket-config$ make secret-init-gpg-socket-config
echo "%Assuan%" > .gitsecret/keys/S.gpg-agent
echo "socket=/tmp/S.gpg-agent" >> .gitsecret/keys/S.gpg-agent
echo "%Assuan%" > .gitsecret/keys/S.gpg-agent.ssh
echo "socket=/tmp/S.gpg-agent.ssh" >> .gitsecret/keys/S.gpg-agent.ssh
echo "extra-socket /tmp/S.gpg-agent.extra" > .gitsecret/keys/gpg-agent.conf
echo "browser-socket /tmp/S.gpg-agent.browser" >> .gitsecret/keys/gpg-agent.conf

容器启动后初始化 gpg

重启容器后,需要初始化 gpg 也就是导入公钥 .dev/gpg-keys/* 和导入私钥 Secret.gpg,不然就无法对文件进行加密和解密。

make gpg-init$ make gpg-init
"C:/Program Files/Git/mingw64/bin/make" -s gpg-import GPG_KEY_FILES="secret.gpg"
gpg: directory '/home/application/.gnupg' created
gpg: keybox '/home/application/.gnupg/pubring.kbx' created
gpg: /home/application/.gnupg/trustdb.gpg: trustdb created
gpg: key BBBE654440E720C1: public key "Alice Doe <alice@example.com>" imported
gpg: key BBBE654440E720C1: secret key imported
gpg: Total number processed: 1
gpg: imported: 1
gpg: secret keys read: 1
gpg: secret keys imported: 1
"C:/Program Files/Git/mingw64/bin/make" -s gpg-import GPG_KEY_FILES=".dev/gpg-keys/*"
gpg: key BBBE654440E720C1: "Alice Doe <alice@example.com>" not changed
gpg: Total number processed: 1
gpg: unchanged: 1

添加新成员

接下来一起看看如何将新成员加入到 git-secret

make secret-add-user EMAIL="alice@example.com"$ make secret-add-user EMAIL="alice@example.com"
"C:/Program Files/Git/mingw64/bin/make" -s git-secret ARGS="tell alice@example.com"
git-secret: done. alice@example.com added as user(s) who know the secret.

验证是否通过:

make secret-show-users$ make secret-show-users
"C:/Program Files/Git/mingw64/bin/make" -s git-secret ARGS="whoknows"
alice@example.com

添加和加密文件

来添加一个新的加密文件 secret_password.txt,创建以下文件:

echo "my_new_secret_password" > secret_password.txt

将其添加到 .gitignore

echo "secret_password.txt" >> .gitignore

将其添加到 git-secret

make secret-add FILE="secret_password.txt"$ make secret-add FILE="secret_password.txt"
"C:/Program Files/Git/mingw64/bin/make" -s git-secret ARGS="add secret_password.txt"
git-secret: 1 item(s) added.

加密所有文件:

make secret-encrypt$ make secret-encrypt
"C:/Program Files/Git/mingw64/bin/make" -s git-secret ARGS="hide"
git-secret: done. 1 of 1 files are hidden.$ ls secret_password.txt.secret
secret_password.txt.secret

解密文件

首先移除 secret_password.txt文件,请运行:

rm secret_password.txt$ rm secret_password.txt$ ls secret_password.txt
ls: cannot access 'secret_password.txt': No such file or directory

然后进行解密:

make secret-decrypt$ make secret-decrypt
"C:/Program Files/Git/mingw64/bin/make" -s git-secret ARGS="reveal -f"
git-secret: done. 1 of 1 files are revealed.$ cat secret_password.txt
my_new_secret_password

注意:如果 gpg 密钥受密码保护(假设密码是 123456),请运行以下命令:

make secret-decrypt-with-password GPG_PASSWORD=123456

此外,还可以将 GPG_PASSWORD 变量加入.make/.env 文件作为本地默认值,这样就不用每次都指定该值,然后可以简单地运行以下命令而不传递 GPG_PASSWORD

make secret-decrypt-with-password

删除文件

可以通过以下方式解密文件:移除之前添加的 secret-password.txt

make secret-remove FILE="secret_password.txt"$ make secret-remove FILE="secret_password.txt"
"C:/Program Files/Git/mingw64/bin/make" -s git-secret ARGS="remove secret_password.txt"
git-secret: removed from index.
git-secret: ensure that files: [secret_password.txt] are now not ignored.

注意:这里既不会自动删除 secret_password.txt 文件,也不会自动删除 secret_password.txt.secret 文件

$ ls -l | grep secret_password.txt
-rw-r--r-- 1 Pascal 197121 3 月 31 日 19 日 14:03 secret_password.txt
-rw-r--r-- 1 Pascal 197121 358 3 月 31 日 14:02 secret_password.txt.secret

即使加密的 secret_password.txt 文件仍然存在,也不会被解密:

$ make secret-decrypt
"C:/Program Files/Git/mingw64/bin/make" -s git-secret ARGS="reveal -f"
git-secret: done. 0 of 0 files are revealed.

移除团队成员

移除团队成员需要通过以下步骤:

make secret-remove-user EMAIL="alice@example.com"$ make secret-remove-user EMAIL="alice@example.com"
"C:/Program Files/Git/mingw64/bin/make" -s git-secret ARGS="killperson alice@example.com"
git-secret: removed keys.
git-secret: now [alice@example.com] do not have an access to the repository.
git-secret: make sure to hide the existing secrets again.

如果团队中还有其他成员留下,需要确保再次加密机密文件:

make secret-encrypt

如果该组已移除全部成员,git-secret 就会报错:

$ make secret-decrypt
"C:/Program Files/Git/mingw64/bin/make" -s git-secret ARGS="reveal -f"
git-secret: abort: no public keys for users found. run 'git secret tell email@address'.
make[1]: *** [.make/01-00-application-setup.mk:57: git-secret] Error 1
make: *** [.make/01-00-application-setup.mk:69: secret-decrypt] Error 2

恭喜你~现在你可以加密和解密机密文件,并存储在 Git 存储库中啦!

git-secret:在 Git 存储库中加密和存储密钥(下)的更多相关文章

  1. git-secret:在 Git 存储库中加密和存储密钥(上)

    当涉及处理机密信息(如密码.令牌.密钥文件等)等,以下问题值得考虑: 安全性十分重要,但高安全性往往伴随着高度的不便. 在团队中,共享某些密钥有时无法避免(因此现在我们需要考虑在多人之间分发和更新密钥 ...

  2. git 从存储库中删除敏感数据(删除文件历史)

    1.如果您的历史记录中还没有包含敏感数据的存储库的本地副本,请将存储库克隆到本地计算机. git clone https://github.com/YOUR-USERNAME/YOUR-REPOSIT ...

  3. 无法将分支 master 发布到远程 origin,因为远程存储库中已存在具有同一名称的分支

    无法将分支 master 发布到远程 origin,因为远程存储库中已存在具有同一名称的分支.发布此分支将导致远程存储库中的分支发生非快进更新. 第一次用oschina的git设置完远程仓库后提交出现 ...

  4. 【Cocos2d-X开发学习笔记】第30期:游戏中数据的存储(下)

    本系列学习教程使用的是cocos2d-x-2.1.4(最新版为3.0alpha0-pre) ,PC开发环境Windows7,C++开发环境VS2010 一.对数据进行编解码 在上一期中,我们使用了CC ...

  5. 本地git部署web连接azure的git存储库

    ​​​本地git部署web 创建本地存储仓库 输入以下命令创建git本地仓库(会在当前目录下生产一个.git的目录) git init 然后提交内容 在git仓库所在的目录下存放好需要的网页文件 将文 ...

  6. g4e基础篇#4 了解Git存储库(Repo)

    章节目录 前言 1. 基础篇: 为什么要使用版本控制系统 Git 分布式版本控制系统的优势 Git 安装和设置 了解Git存储库(Repo) 起步 1 – 创建分支和保存代码 起步 2 – 了解Git ...

  7. git rebase VS git merge? 更优雅的 git 合并方式值得拥有

    写在前面 如果你不能很好的应用 Git,那么这里为你提供一个非常棒的 Git 在线练习工具 Git Online ,你可以更直观的看到你所使用的命令会产生什么效果 另外,你在使用 Git 合并分支时只 ...

  8. git rebase VS git merge

    git rebase VS git merge 写在前面 如果你不能很好的应用 Git,那么这里为你提供一个非常棒的 Git 在线练习工具 Git Online(回复公众号「工具」,获取更多内容) , ...

  9. Git安装/VScode+Git+Github

    Git安装/VScode+Git+Github 1. 相关简介 git 版本控制工具,支持该工具的网站有Github.BitBucket.Gitorious.国内的OS China仓库.Csdn仓库等 ...

随机推荐

  1. Ubuntu 20.04安装Docker

    Docker学习系列文章 入门必备:十本你不容错过的Docker入门到精通书籍推荐 day1.全面的Docker快速入门教程 day2.CentOS 8.4安装Docker day3.Windows1 ...

  2. 八个解决你80%需求的CSS动画库

    八个解决你80%需求的CSS动画库 点击打开视频讲解 在学习和工作的过程中,我们总免不了要写各种各样的css动画,给某个部分添加动画效果,如果觉得自己写的动画效果单一乏味,不妨试试这8个CSS动画库, ...

  3. luogu1419 寻找段落 (二分,单调队列)

    单调队列存坐标 #include <iostream> #include <cstdio> #include <cstring> #include <algo ...

  4. django的csrf跨站请求伪造

    1.什么是跨站请求伪造 请看图: 我们自行写了一个网站模仿中国银行,用户不知道是否是真的中国银行,并且提交了转账信息,生成一个form表单,向银行服务器发送转账请求,这个form表单和正规银行网站的f ...

  5. Flutter 开启 Windows、macOS 平台支持的命令

    Flutter 的多平台支持除了 Android 和 iOS 是默认开启的以外,比如 Windows.Linux 平台的支持需要手动开启. Flutter config 命令集中,有以下参数是对于平台 ...

  6. [java]基础学习HELLOWORLD系列

    (一)手把手教你做JDK环境变量配置 步骤 1 : 首先看配置成功后的效果 点WIN键->运行(或者使用win+r) 输入cmd命令 输入java -version 注: -version是小写 ...

  7. CCF NOI Online 2021 提高组 T2 积木小赛 (子序列自动机+后缀自动机,O(n^2))

    题面 Alice 和 Bob 最近热衷于玩一个游戏--积木小赛. Alice 和 Bob 初始时各有 n 块积木从左至右排成一排,每块积木都被标上了一个英文小写字母. Alice 可以从自己的积木中丢 ...

  8. 【MySQL】DDL因Waiting for table metadata lock卡住

    在数据库空闲时间,对表做碎片整理: alter table my_abc engine=innodb; 发现会话被阻塞,显示状态是: Waiting for table metadata lock 手 ...

  9. 记录一下~~~Linux配置定时任务备份数据库dmp文件

    1.创建备份目录: mkdir -p /dcits/sx_xmz/sx_data_bak chown -R oracle18c:oinstall /dcits/sx_xmz/sx_data_bak 2 ...

  10. Hybrid app本地开发如何调用JSBridge

    前天同事问我公司内部的小程序怎么对接的,我回忆了一下,简单记录了一下前端同学需要注意的点. 背后还有小程序架构.网络策略等等.当时恰逢小程序架构调整,(老架构的时候我就发现了有一个问题点可以优化,但是 ...