标签归档:git

Python Git模块的经验?[关闭]

问题:Python Git模块的经验?[关闭]

人们对Python的任何Git模块有何经验?(我知道GitPython,PyGit和Dulwich-如果您知道其他人,请随意提及。)

我正在编写一个程序,该程序必须与Git存储库进行交互(添加,删除,提交),但是没有使用Git的经验,所以我要寻找的一件事是关于Git的易用性/理解性。

我主要感兴趣的其他内容是库的成熟度和完整性,合理的错误缺失,持续的开发以及文档和开发人员的帮助。

如果您有其他我想/需要知道的事情,请随时提及。

What are people’s experiences with any of the Git modules for Python? (I know of GitPython, PyGit, and Dulwich – feel free to mention others if you know of them.)

I am writing a program which will have to interact (add, delete, commit) with a Git repository, but have no experience with Git, so one of the things I’m looking for is ease of use/understanding with regards to Git.

The other things I’m primarily interested in are maturity and completeness of the library, a reasonable lack of bugs, continued development, and helpfulness of the documentation and developers.

If you think of something else I might want/need to know, please feel free to mention it.


回答 0

虽然这个问题是在不久前提出的,但我当时还不知道库的状态,但是值得搜索的人提到,GitPython在抽象命令行工具方面做得很好,因此您无需使用子流程。您可以使用一些有用的内置抽象,但是对于其他所有事情,您都可以执行以下操作:

import git
repo = git.Repo( '/home/me/repodir' )
print repo.git.status()
# checkout and track a remote branch
print repo.git.checkout( 'origin/somebranch', b='somebranch' )
# add a file
print repo.git.add( 'somefile' )
# commit
print repo.git.commit( m='my commit message' )
# now we are one commit ahead
print repo.git.status()

GitPython中的其他所有功能都使其更易于浏览。我对此库非常满意,并赞赏它是基础git工具的包装。

更新:我已经切换到不仅使用git,而且还使用python中需要的大多数命令行实用程序使用sh模块。为了复制上面的内容,我将改为执行以下操作:

import sh
git = sh.git.bake(_cwd='/home/me/repodir')
print git.status()
# checkout and track a remote branch
print git.checkout('-b', 'somebranch')
# add a file
print git.add('somefile')
# commit
print git.commit(m='my commit message')
# now we are one commit ahead
print git.status()

While this question was asked a while ago and I don’t know the state of the libraries at that point, it is worth mentioning for searchers that GitPython does a good job of abstracting the command line tools so that you don’t need to use subprocess. There are some useful built in abstractions that you can use, but for everything else you can do things like:

import git
repo = git.Repo( '/home/me/repodir' )
print repo.git.status()
# checkout and track a remote branch
print repo.git.checkout( 'origin/somebranch', b='somebranch' )
# add a file
print repo.git.add( 'somefile' )
# commit
print repo.git.commit( m='my commit message' )
# now we are one commit ahead
print repo.git.status()

Everything else in GitPython just makes it easier to navigate. I’m fairly well satisfied with this library and appreciate that it is a wrapper on the underlying git tools.

UPDATE: I’ve switched to using the sh module for not just git but most commandline utilities I need in python. To replicate the above I would do this instead:

import sh
git = sh.git.bake(_cwd='/home/me/repodir')
print git.status()
# checkout and track a remote branch
print git.checkout('-b', 'somebranch')
# add a file
print git.add('somefile')
# commit
print git.commit(m='my commit message')
# now we are one commit ahead
print git.status()

回答 1

我以为我会回答自己的问题,因为我所采取的途径与答案中所建议的不同。尽管如此,感谢那些回答。

首先,简要介绍一下我在GitPython,PyGit和Dulwich的经验:

  • GitPython:下载后,我将其导入并初始化了适当的对象。但是,尝试执行本教程中建议的操作会导致错误。缺乏更多文档,我转向其他地方。
  • PyGit:这甚至都不会导入,而且我找不到任何文档。
  • 德威:似乎是最有前途的(至少就我想要和看到的而言)。与GitPython相比,我在其中取得了一些进步,因为它的卵来自Python源。但是,过了一会儿,我决定尝试做一下可能会更容易。

同样,StGit看起来很有趣,但是我需要将功能提取到一个单独的模块中,并且不希望现在等待它发生。

在比使上面的三个模块正常工作所花费的时间少得多的时间内,我设法通过子流程模块使git命令起作用,例如

def gitAdd(fileName, repoDir):
    cmd = ['git', 'add', fileName]
    p = subprocess.Popen(cmd, cwd=repoDir)
    p.wait()

gitAdd('exampleFile.txt', '/usr/local/example_git_repo_dir')

这还没有完全集成到我的程序中,但是除了速度(我有时会处理数百甚至数千个文件)之外,我没有预料到任何问题。

也许我只是没有耐心让Dulwich或GitPython正常运行。就是说,我希望这些模块能够得到更多的开发并且很快会有用。

I thought I would answer my own question, since I’m taking a different path than suggested in the answers. Nonetheless, thanks to those who answered.

First, a brief synopsis of my experiences with GitPython, PyGit, and Dulwich:

  • GitPython: After downloading, I got this imported and the appropriate object initialized. However, trying to do what was suggested in the tutorial led to errors. Lacking more documentation, I turned elsewhere.
  • PyGit: This would not even import, and I could find no documentation.
  • Dulwich: Seems to be the most promising (at least for what I wanted and saw). I made some progress with it, more than with GitPython, since its egg comes with Python source. However, after a while, I decided it may just be easier to try what I did.

Also, StGit looks interesting, but I would need the functionality extracted into a separate module and do not want wait for that to happen right now.

In (much) less time than I spent trying to get the three modules above working, I managed to get git commands working via the subprocess module, e.g.

def gitAdd(fileName, repoDir):
    cmd = ['git', 'add', fileName]
    p = subprocess.Popen(cmd, cwd=repoDir)
    p.wait()

gitAdd('exampleFile.txt', '/usr/local/example_git_repo_dir')

This isn’t fully incorporated into my program yet, but I’m not anticipating a problem, except maybe speed (since I’ll be processing hundreds or even thousands of files at times).

Maybe I just didn’t have the patience to get things going with Dulwich or GitPython. That said, I’m hopeful the modules will get more development and be more useful soon.


回答 2

我建议pygit2-它使用出色的libgit2绑定

I’d recommend pygit2 – it uses the excellent libgit2 bindings


回答 3

这是一个非常老的问题,在寻找Git库时,我发现了今年(2013年)制造的一个名为Gittle的库

它对我很有用(我尝试过的其他地方都比较薄弱),而且似乎涵盖了大多数常见操作。

自述文件中的一些示例:

from gittle import Gittle

# Clone a repository
repo_path = '/tmp/gittle_bare'
repo_url = 'git://github.com/FriendCode/gittle.git'
repo = Gittle.clone(repo_url, repo_path)

# Stage multiple files
repo.stage(['other1.txt', 'other2.txt'])

# Do the commit
repo.commit(name="Samy Pesse", email="samy@friendco.de", message="This is a commit")

# Authentication with RSA private key
key_file = open('/Users/Me/keys/rsa/private_rsa')
repo.auth(pkey=key_file)

# Do push
repo.push()

This is a pretty old question, and while looking for Git libraries, I found one that was made this year (2013) called Gittle.

It worked great for me (where the others I tried were flaky), and seems to cover most of the common actions.

Some examples from the README:

from gittle import Gittle

# Clone a repository
repo_path = '/tmp/gittle_bare'
repo_url = 'git://github.com/FriendCode/gittle.git'
repo = Gittle.clone(repo_url, repo_path)

# Stage multiple files
repo.stage(['other1.txt', 'other2.txt'])

# Do the commit
repo.commit(name="Samy Pesse", email="samy@friendco.de", message="This is a commit")

# Authentication with RSA private key
key_file = open('/Users/Me/keys/rsa/private_rsa')
repo.auth(pkey=key_file)

# Do push
repo.push()

回答 4

也许有帮助,但是Bazaar和Mercurial都使用dulwich来实现Git的互操作性。

Dulwich在某种意义上可能与另一个有所不同,因为它是python中git的重新实现。另一个可能只是Git命令的包装器(因此从较高的角度来看,它可能更易于使用:commit / add / delete),这可能意味着它们的API与git的命令行非常接近,因此您需要获得有关Git的经验。

Maybe it helps, but Bazaar and Mercurial are both using dulwich for their Git interoperability.

Dulwich is probably different than the other in the sense that’s it’s a reimplementation of git in python. The other might just be a wrapper around Git’s commands (so it could be simpler to use from a high level point of view: commit/add/delete), it probably means their API is very close to git’s command line so you’ll need to gain experience with Git.


回答 5

更新的答案反映了更改的时间:

GitPython当前是最容易使用的。它支持许多git plumbing命令的包装,并具有可插入的对象数据库(其中的一个是德威奇),并且如果未实现命令,则提供了一个简单的api,可以用于命令行。例如:

repo = Repo('.')
repo.checkout(b='new_branch')

这调用:

bash$ git checkout -b new_branch

德威也不错,但水平要低得多。使用它有点痛苦,因为它需要在管道级上对git对象进行操作,并且没有通常想要的精美瓷器。但是,如果您打算修改git的任何部分,或者使用git-receive-pack和git-upload-pack,则需要使用dulwich。

An updated answer reflecting changed times:

GitPython currently is the easiest to use. It supports wrapping of many git plumbing commands and has pluggable object database (dulwich being one of them), and if a command isn’t implemented, provides an easy api for shelling out to the command line. For example:

repo = Repo('.')
repo.checkout(b='new_branch')

This calls:

bash$ git checkout -b new_branch

Dulwich is also good but much lower level. It’s somewhat of a pain to use because it requires operating on git objects at the plumbing level and doesn’t have nice porcelain that you’d normally want to do. However, if you plan on modifying any parts of git, or use git-receive-pack and git-upload-pack, you need to use dulwich.


回答 6

为了完整起见,http://github.com/alex/pyvcs/是所有dvc的抽象层。它使用dulwich,但与其他dvc提供互操作。

For the sake of completeness, http://github.com/alex/pyvcs/ is an abstraction layer for all dvcs’s. It uses dulwich, but provides interop with the other dvcs’s.


回答 7

这是“ git status”的真正快速实现:

import os
import string
from subprocess import *

repoDir = '/Users/foo/project'

def command(x):
    return str(Popen(x.split(' '), stdout=PIPE).communicate()[0])

def rm_empty(L): return [l for l in L if (l and l!="")]

def getUntracked():
    os.chdir(repoDir)
    status = command("git status")
    if "# Untracked files:" in status:
        untf = status.split("# Untracked files:")[1][1:].split("\n")
        return rm_empty([x[2:] for x in untf if string.strip(x) != "#" and x.startswith("#\t")])
    else:
        return []

def getNew():
    os.chdir(repoDir)
    status = command("git status").split("\n")
    return [x[14:] for x in status if x.startswith("#\tnew file:   ")]

def getModified():
    os.chdir(repoDir)
    status = command("git status").split("\n")
    return [x[14:] for x in status if x.startswith("#\tmodified:   ")]

print("Untracked:")
print( getUntracked() )
print("New:")
print( getNew() )
print("Modified:")
print( getModified() )

Here’s a really quick implementation of “git status”:

import os
import string
from subprocess import *

repoDir = '/Users/foo/project'

def command(x):
    return str(Popen(x.split(' '), stdout=PIPE).communicate()[0])

def rm_empty(L): return [l for l in L if (l and l!="")]

def getUntracked():
    os.chdir(repoDir)
    status = command("git status")
    if "# Untracked files:" in status:
        untf = status.split("# Untracked files:")[1][1:].split("\n")
        return rm_empty([x[2:] for x in untf if string.strip(x) != "#" and x.startswith("#\t")])
    else:
        return []

def getNew():
    os.chdir(repoDir)
    status = command("git status").split("\n")
    return [x[14:] for x in status if x.startswith("#\tnew file:   ")]

def getModified():
    os.chdir(repoDir)
    status = command("git status").split("\n")
    return [x[14:] for x in status if x.startswith("#\tmodified:   ")]

print("Untracked:")
print( getUntracked() )
print("New:")
print( getNew() )
print("Modified:")
print( getModified() )

回答 8

PTBNL的答案对我来说非常完美。我为Windows用户提供了更多功能。

import time
import subprocess
def gitAdd(fileName, repoDir):
    cmd = 'git add ' + fileName
    pipe = subprocess.Popen(cmd, shell=True, cwd=repoDir,stdout = subprocess.PIPE,stderr = subprocess.PIPE )
    (out, error) = pipe.communicate()
    print out,error
    pipe.wait()
    return 

def gitCommit(commitMessage, repoDir):
    cmd = 'git commit -am "%s"'%commitMessage
    pipe = subprocess.Popen(cmd, shell=True, cwd=repoDir,stdout = subprocess.PIPE,stderr = subprocess.PIPE )
    (out, error) = pipe.communicate()
    print out,error
    pipe.wait()
    return 
def gitPush(repoDir):
    cmd = 'git push '
    pipe = subprocess.Popen(cmd, shell=True, cwd=repoDir,stdout = subprocess.PIPE,stderr = subprocess.PIPE )
    (out, error) = pipe.communicate()
    pipe.wait()
    return 

temp=time.localtime(time.time())
uploaddate= str(temp[0])+'_'+str(temp[1])+'_'+str(temp[2])+'_'+str(temp[3])+'_'+str(temp[4])

repoDir='d:\\c_Billy\\vfat\\Programming\\Projector\\billyccm' # your git repository , windows your need to use double backslash for right directory.
gitAdd('.',repoDir )
gitCommit(uploaddate, repoDir)
gitPush(repoDir)

PTBNL’s Answer is quite perfect for me. I make a little more for Windows user.

import time
import subprocess
def gitAdd(fileName, repoDir):
    cmd = 'git add ' + fileName
    pipe = subprocess.Popen(cmd, shell=True, cwd=repoDir,stdout = subprocess.PIPE,stderr = subprocess.PIPE )
    (out, error) = pipe.communicate()
    print out,error
    pipe.wait()
    return 

def gitCommit(commitMessage, repoDir):
    cmd = 'git commit -am "%s"'%commitMessage
    pipe = subprocess.Popen(cmd, shell=True, cwd=repoDir,stdout = subprocess.PIPE,stderr = subprocess.PIPE )
    (out, error) = pipe.communicate()
    print out,error
    pipe.wait()
    return 
def gitPush(repoDir):
    cmd = 'git push '
    pipe = subprocess.Popen(cmd, shell=True, cwd=repoDir,stdout = subprocess.PIPE,stderr = subprocess.PIPE )
    (out, error) = pipe.communicate()
    pipe.wait()
    return 

temp=time.localtime(time.time())
uploaddate= str(temp[0])+'_'+str(temp[1])+'_'+str(temp[2])+'_'+str(temp[3])+'_'+str(temp[4])

repoDir='d:\\c_Billy\\vfat\\Programming\\Projector\\billyccm' # your git repository , windows your need to use double backslash for right directory.
gitAdd('.',repoDir )
gitCommit(uploaddate, repoDir)
gitPush(repoDir)

回答 9

StGit的git交互库部分实际上非常好。但是,它不是作为单独的软件包分解的,但是,如果有足够的兴趣,我相信可以解决。

它具有非常好的抽象,用于表示提交,树等,以及用于创建新的提交和树。

The git interaction library part of StGit is actually pretty good. However, it isn’t broken out as a separate package but if there is sufficient interest, I’m sure that can be fixed.

It has very nice abstractions for representing commits, trees etc, and for creating new commits and trees.


回答 10

记录下来,前面提到的Git Python库似乎都没有包含“ git status”等效项,这实际上是我唯一想要的,因为通过子进程处理其余git命令非常容易。

For the record, none of the aforementioned Git Python libraries seem to contain a “git status” equivalent, which is really the only thing I would want since dealing with the rest of the git commands via subprocess is so easy.


如何将我的秘密密钥和密码安全地保存在版本控制系统中?

问题:如何将我的秘密密钥和密码安全地保存在版本控制系统中?

我在版本控制系统中保留了重要的设置,例如开发和生产服务器的主机名和端口。但是我知道,将秘密(例如私钥和数据库密码)保存在VCS存储库中是一种不好的做法

但是像其他设置一样,密码似乎应该进行版本控制。那么保持密码版本控制的正确方法什么?

我想这将涉及保持秘密,在自己的“秘密设置”文件,并有文件的加密和版本控制。但是什么技术呢?以及如何正确执行此操作?有没有更好的方法可以完全解决这个问题?


我通常会问这个问题,但是在我的特定实例中,我想使用gitgithub存储Django / Python站点的秘密密钥和密码。

同样,当我使用git推/拉时,理想的解决方案也会做一些神奇的事情-例如,如果加密的密码文件更改了运行的脚本,该脚本会要求输入密码并将其解密到位。


编辑:为清楚起见,我正在询问在哪里存储的生产秘密。

I keep important settings like the hostnames and ports of development and production servers in my version control system. But I know that it’s bad practice to keep secrets (like private keys and database passwords) in a VCS repository.

But passwords–like any other setting–seem like they should be versioned. So what is the proper way to keep passwords version controlled?

I imagine it would involve keeping the secrets in their own “secrets settings” file and having that file encrypted and version controlled. But what technologies? And how to do this properly? Is there a better way entirely to go about it?


I ask the question generally, but in my specific instance I would like to store secret keys and passwords for a Django/Python site using git and github.

Also, an ideal solution would do something magical when I push/pull with git–e.g., if the encrypted passwords file changes a script is run which asks for a password and decrypts it into place.


EDIT: For clarity, I am asking about where to store production secrets.

回答 0

您完全想在保持版本控制的同时加密敏感设置文件是正确的。正如您提到的,最好的解决方案是Git在推送某些敏感文件时透明地对其进行加密,以便在本地(即在具有证书的任何计算机上)可以使用设置文件,但Git或Dropbox或在VC下存储文件无法读取纯文本信息。

推/拉期间的透明加密/解密教程

这个要点https://gist.github.com/873637显示了有关如何将Git的smudge / clean过滤器驱动程序与openssl一起使用的教程,以透明方式加密推送的文件。您只需要进行一些初始设置即可。

工作原理摘要

您基本上将创建一个.gitencrypt包含3个bash脚本的文件夹,

clean_filter_openssl 
smudge_filter_openssl 
diff_filter_openssl 

Git用于解密,加密和支持Git差异。在这些脚本中定义了主密码和盐(已修复!),您必须确保.gitencrypt从未被实际推送。示例clean_filter_openssl脚本:

#!/bin/bash

SALT_FIXED=<your-salt> # 24 or less hex characters
PASS_FIXED=<your-passphrase>

openssl enc -base64 -aes-256-ecb -S $SALT_FIXED -k $PASS_FIXED

smudge_filter_open_ssl和类似diff_filter_oepnssl。参见要点。

您的带有敏感信息的仓库应该有一个.gitattribute文件(未加密并包含在仓库中),该文件引用.gitencrypt目录(该目录包含Git透明地加密/解密项目所需的所有内容),并且该文件存在于本地计算机上。

.gitattribute 内容:

* filter=openssl diff=openssl
[merge]
    renormalize = true

最后,您还需要将以下内容添加到.git/config文件中

[filter "openssl"]
    smudge = ~/.gitencrypt/smudge_filter_openssl
    clean = ~/.gitencrypt/clean_filter_openssl
[diff "openssl"]
    textconv = ~/.gitencrypt/diff_filter_openssl

现在,当您将包含敏感信息的存储库推送到远程存储库时,文件将被透明加密。当您从具有.gitencrypt目录(包含密码)的本地计算机中提取文件时,文件将被透明解密。

笔记

我应该注意,本教程没有描述仅加密敏感设置文件的方法。这将透明地加密推送到远程VC主机的整个存储库,并解密整个存储库,以便在本地对其进行完全解密。为了实现所需的行为,您可以将一个或多个项目的敏感文件放在一个sensitive_settings_repo中。如果您确实需要将敏感文件存储在同一存储库中,则可以研究这种透明加密技术如何与Git子模块http://git-scm.com/book/en/Git-Tools-Submodules一起使用。

如果攻击者可以访问许多加密的存储库/文件,则在理论上使用固定的密码短语可能会导致暴力破解漏洞。海事组织,这种可能性非常低。正如本教程底部提到的那样,不使用固定密码短语将导致不同机器上的本地回购版本始终显示“ git status”已发生更改。

You’re exactly right to want to encrypt your sensitive settings file while still maintaining the file in version control. As you mention, the best solution would be one in which Git will transparently encrypt certain sensitive files when you push them so that locally (i.e. on any machine which has your certificate) you can use the settings file, but Git or Dropbox or whoever is storing your files under VC does not have the ability to read the information in plaintext.

Tutorial on Transparent Encryption/Decryption during Push/Pull

This gist https://gist.github.com/873637 shows a tutorial on how to use the Git’s smudge/clean filter driver with openssl to transparently encrypt pushed files. You just need to do some initial setup.

Summary of How it Works

You’ll basically be creating a .gitencrypt folder containing 3 bash scripts,

clean_filter_openssl 
smudge_filter_openssl 
diff_filter_openssl 

which are used by Git for decryption, encryption, and supporting Git diff. A master passphrase and salt (fixed!) is defined inside these scripts and you MUST ensure that .gitencrypt is never actually pushed. Example clean_filter_openssl script:

#!/bin/bash

SALT_FIXED=<your-salt> # 24 or less hex characters
PASS_FIXED=<your-passphrase>

openssl enc -base64 -aes-256-ecb -S $SALT_FIXED -k $PASS_FIXED

Similar for smudge_filter_open_ssl and diff_filter_oepnssl. See Gist.

Your repo with sensitive information should have a .gitattribute file (unencrypted and included in repo) which references the .gitencrypt directory (which contains everything Git needs to encrypt/decrypt the project transparently) and which is present on your local machine.

.gitattribute contents:

* filter=openssl diff=openssl
[merge]
    renormalize = true

Finally, you will also need to add the following content to your .git/config file

[filter "openssl"]
    smudge = ~/.gitencrypt/smudge_filter_openssl
    clean = ~/.gitencrypt/clean_filter_openssl
[diff "openssl"]
    textconv = ~/.gitencrypt/diff_filter_openssl

Now, when you push the repository containing your sensitive information to a remote repository, the files will be transparently encrypted. When you pull from a local machine which has the .gitencrypt directory (containing your passphrase), the files will be transparently decrypted.

Notes

I should note that this tutorial does not describe a way to only encrypt your sensitive settings file. This will transparently encrypt the entire repository that is pushed to the remote VC host and decrypt the entire repository so it is entirely decrypted locally. To achieve the behavior you want, you could place sensitive files for one or many projects in one sensitive_settings_repo. You could investigate how this transparent encryption technique works with Git submodules http://git-scm.com/book/en/Git-Tools-Submodules if you really need the sensitive files to be in the same repository.

The use of a fixed passphrase could theoretically lead to brute-force vulnerabilities if attackers had access to many encrypted repos/files. IMO, the probability of this is very low. As a note at the bottom of this tutorial mentions, not using a fixed passphrase will result in local versions of a repo on different machines always showing that changes have occurred with ‘git status’.


回答 1

Heroku推动使用环境变量进行设置和密钥:

处理此类config var的传统方法是将它们放在源代码下-放在某种属性文件中。这是一个容易出错的过程,对于经常需要使用应用程序特定配置维护单独的(和私有的)分支的开源应用程序而言,这尤其复杂。

更好的解决方案是使用环境变量,并将键保留在代码之外。在传统主机上或在本地工作,您可以在bashrc中设置环境变量。在Heroku上,您使用config vars。

借助Foreman和.env文件,Heroku提供了令人羡慕的工具链来导出,导入和同步环境变量。


就个人而言,我认为将秘密密钥与代码一起保存是错误的。从根本上说,它与源代码管理不一致,因为密钥是针对代码外部的服务。一个好处是开发人员可以克隆HEAD并运行应用程序而无需进行任何设置。但是,假设开发人员检查了该代码的历史版本。他们的副本将包含去年的数据库密码,因此该应用程序将无法使用今天的数据库。

使用上面的Heroku方法,开发人员可以签出去年的应用程序,使用今天的密钥对其进行配置,然后针对当今的数据库成功运行它。

Heroku pushes the use of environment variables for settings and secret keys:

The traditional approach for handling such config vars is to put them under source – in a properties file of some sort. This is an error-prone process, and is especially complicated for open source apps which often have to maintain separate (and private) branches with app-specific configurations.

A better solution is to use environment variables, and keep the keys out of the code. On a traditional host or working locally you can set environment vars in your bashrc. On Heroku, you use config vars.

With Foreman and .env files Heroku provide an enviable toolchain to export, import and synchronise environment variables.


Personally, I believe it’s wrong to save secret keys alongside code. It’s fundamentally inconsistent with source control, because the keys are for services extrinsic to the the code. The one boon would be that a developer can clone HEAD and run the application without any setup. However, suppose a developer checks out a historic revision of the code. Their copy will include last year’s database password, so the application will fail against today’s database.

With the Heroku method above, a developer can checkout last year’s app, configure it with today’s keys, and run it successfully against today’s database.


回答 2

我认为最干净的方法是使用环境变量。例如,您不必处理.dist文件,生产环境中的项目状态将与本地计算机的状态相同。

我建议阅读“十二要素应用程序 ”的配置章节,如果您有兴趣的话,也阅读其他章节。

The cleanest way in my opinion is to use environment variables. You won’t have to deal with .dist files for example, and the project state on the production environment would be the same as your local machine’s.

I recommend reading The Twelve-Factor App‘s config chapter, the others too if you’re interested.


回答 3

一种选择是将项目绑定的凭据放入加密的容器(TrueCrypt或Keepass)中并推送。

从下面的评论中更新为答案:

有趣的问题顺便说一句。我刚刚发现了这一点:github.com/shadowhand/git-encrypt对于自动加密而言,它看起来很有希望

An option would be to put project-bound credentials into an encrypted container (TrueCrypt or Keepass) and push it.

Update as answer from my comment below:

Interesting question btw. I just found this: github.com/shadowhand/git-encrypt which looks very promising for automatic encryption


回答 4

我建议为此使用配置文件,而不要对其进行版本控制。

但是,您可以版本文件的示例。

我看不到共享开发设置的任何问题。根据定义,它不应包含任何有价值的数据。

I suggest using configuration files for that and to not version them.

You can however version examples of the files.

I don’t see any problem of sharing development settings. By definition it should contain no valuable data.


回答 5

BlackBox是由StackExchange最近发布的,虽然我尚未使用它,但它似乎可以完全解决问题并支持此问题中要求的功能。

https://github.com/StackExchange/blackbox的描述中:

将机密安全存储在VCS仓库中(即Git或Mercurial)。这些命令使您可以轻松地对存储库中的特定文件进行GPG加密,以便在存储库中对它们进行“静态加密”。但是,使用脚本可以轻松地在需要查看或编辑脚本时对其进行解密,并可以将其解密以用于生产。

BlackBox was recently released by StackExchange and while I have yet to use it, it seems to exactly address the problems and support the features requested in this question.

From the description on https://github.com/StackExchange/blackbox:

Safely store secrets in a VCS repo (i.e. Git or Mercurial). These commands make it easy for you to GPG encrypt specific files in a repo so they are “encrypted at rest” in your repository. However, the scripts make it easy to decrypt them when you need to view or edit them, and decrypt them for for use in production.


回答 6

自从问了这个问题之后,我就确定了一个解决方案,该解决方案是在与一小群人一起开发小型应用程序时使用的。

git-crypt

git-crypt使用GPG对文件名进行匹配的透明加密。例如,如果您添加到.gitattributes文件中…

*.secret.* filter=git-crypt diff=git-crypt

…然后,像这样的文件config.secret.json将始终通过加密方式推送到远程存储库,但在本地文件系统上保持未加密状态。

如果我想向您的存储库添加一个新的GPG密钥(一个人),可以解密受保护的文件,请运行git-crypt add-gpg-user <gpg_user_key>。这将创建一个新的提交。新用户将能够解密后续提交。

Since asking this question I have settled on a solution, which I use when developing small application with a small team of people.

git-crypt

git-crypt uses GPG to transparently encrypt files when their names match certain patterns. For intance, if you add to your .gitattributes file…

*.secret.* filter=git-crypt diff=git-crypt

…then a file like config.secret.json will always be pushed to remote repos with encryption, but remain unencrypted on your local file system.

If I want to add a new GPG key (a person) to your repo which can decrypt the protected files then run git-crypt add-gpg-user <gpg_user_key>. This creates a new commit. The new user will be able to decrypt subsequent commits.


回答 7

我通常会问这个问题,但是在我的特定实例中,我想使用git和github存储Django / Python站点的秘密密钥和密码。

不,只是不要,即使这是您的私人存储库,并且您从不打算共享它,也不要。

您应该创建一个local_settings.py,将其放在VCS上忽略,然后在settings.py中执行类似的操作

from local_settings import DATABASES, SECRET_KEY
DATABASES = DATABASES

SECRET_KEY = SECRET_KEY

如果您的机密设置用途广泛,我很想说您做错了

I ask the question generally, but in my specific instance I would like to store secret keys and passwords for a Django/Python site using git and github.

No, just don’t, even if it’s your private repo and you never intend to share it, don’t.

You should create a local_settings.py put it on VCS ignore and in your settings.py do something like

from local_settings import DATABASES, SECRET_KEY
DATABASES = DATABASES

SECRET_KEY = SECRET_KEY

If your secrets settings are that versatile, I am eager to say you’re doing something wrong


回答 8

编辑:我假设您想跟踪以前的密码版本-例如,对于防止密码重用等的脚本。

我认为GnuPG是最好的方法-已经在一个与git相关的项目(git-annex)中用于加密存储在云服务上的存储库内容。GnuPG(gnu pgp)提供了非常强大的基于密钥的加密。

  1. 您将密钥保留在本地计算机上。
  2. 您将’mypassword’添加到忽略的文件。
  3. 在pre-commit挂钩上,您将mypassword文件加密为git跟踪的mypassword.gpg文件,并将其添加到提交中。
  4. 在合并后钩子上,您只需将mypassword.gpg解密为mypassword。

现在,如果您的“ mypassword”文件未更改,则将使用相同的密文对其进行加密,并且不会将其添加到索引中(无冗余)。稍加修改mypassword会导致密文截然不同,并且登台区域中的mypassword.gpg与存储库中的密文有很大不同,因此将被添加到提交中。即使攻击者掌握了您的gpg密钥,他仍然需要暴力破解密码。如果攻击者可以使用密文访问远程存储库,则他可以比较一堆密文,但是它们的数量不足以给他带来任何不可忽视的优势。

稍后,您可以使用.gitattributes为您的密码退出git diff提供即时解密。

您也可以为不同类型的密码等设置单独的密钥。

EDIT: I assume you want to keep track of your previous passwords versions – say, for a script that would prevent password reusing etc.

I think GnuPG is the best way to go – it’s already used in one git-related project (git-annex) to encrypt repository contents stored on cloud services. GnuPG (gnu pgp) provides a very strong key-based encryption.

  1. You keep a key on your local machine.
  2. You add ‘mypassword’ to ignored files.
  3. On pre-commit hook you encrypt the mypassword file into the mypassword.gpg file tracked by git and add it to the commit.
  4. On post-merge hook you just decrypt mypassword.gpg into mypassword.

Now if your ‘mypassword’ file did not change then encrypting it will result with same ciphertext and it won’t be added to the index (no redundancy). Slightest modification of mypassword results in radically different ciphertext and mypassword.gpg in staging area differs a lot from the one in repository, thus will be added to the commit. Even if the attacker gets a hold of your gpg key he still needs to bruteforce the password. If the attacker gets an access to remote repository with ciphertext he can compare a bunch of ciphertexts, but their number won’t be sufficient to give him any non-negligible advantage.

Later on you can use .gitattributes to provide an on-the-fly decryption for quit git diff of your password.

Also you can have separate keys for different types of passwords etc.


回答 9

通常,我将密码分隔为配置文件。并使其成为dist。

/yourapp
    main.py
    default.cfg.dist

当我跑步时main.py,输入真实密码default.cfg该副本中。

ps。当您使用git或hg时。您可以忽略*.cfg要制作的文件.gitignore.hgignore

Usually, i seperate password as a config file. and make them dist.

/yourapp
    main.py
    default.cfg.dist

And when i run main.py, put the real password in default.cfg that copied.

ps. when you work with git or hg. you can ignore *.cfg files to make .gitignore or .hgignore


回答 10

提供一种方法来覆盖配置

这是管理您签入的配置的一组合理默认值的最佳方法,而无需完成配置或包含主机名和凭据之类的内容。有几种方法可以覆盖默认配置。

环境变量(如其他人已经提到的)是做到这一点的一种方法。

最好的方法是寻找一个覆盖默认配置值的外部配置文件。这使您可以通过诸如Chef,Puppet或Cfengine之类的配置管理系统来管理外部配置。配置管理是与代码库分开进行配置管理的标准答案,因此您无需发布即可在单个主机或一组主机上更新配置。

仅供参考:加密凭证并非总是最佳做法,尤其是在资源有限的地方。可能情况是,加密凭据不会为您带来额外的风险缓解,而只会增加不必要的复杂性。在做出决定之前,请确保您进行了正确的分析。

Provide a way to override the config

This is the best way to manage a set of sane defaults for the config you checkin without requiring the config be complete, or contain things like hostnames and credentials. There are a few ways to override default configs.

Environment variables (as others have already mentioned) are one way of doing it.

The best way is to look for an external config file that overrides the default config values. This allows you to manage the external configs via a configuration management system like Chef, Puppet or Cfengine. Configuration management is the standard answer for the management of configs separate from the codebase so you don’t have to do a release to update the config on a single host or a group of hosts.

FYI: Encrypting creds is not always a best practice, especially in a place with limited resources. It may be the case that encrypting creds will gain you no additional risk mitigation and simply add an unnecessary layer of complexity. Make sure you do the proper analysis before making a decision.


回答 11

使用例如GPG加密密码文件。将密钥添加到本地计算机和服务器上。解密文件并将其放在您的repo文件夹之外。

我使用homefolder中的passwords.conf。在每次部署中,此文件都会更新。

Encrypt the passwords file, using for example GPG. Add the keys on your local machine and on your server. Decrypt the file and put it outside your repo folders.

I use a passwords.conf, located in my homefolder. On every deploy this file gets updated.


回答 12

不可以,私钥和密码不属于版本控制。当大多数情况下并非所有人都应该有权访问这些服务时,没有理由让每个人都拥有对存储库的读取访问权的负担,因为他们知道生产中使用的敏感服务凭证。

从Django 1.4开始,您的Django项目现在附带了project.wsgi定义application对象的模块,这是开始强制使用a的理想场所project.local包含特定于站点的配置设置模块。

该设置模块从版本控制中被忽略,但是在将项目实例作为WSGI应用程序运行时(在生产环境中通常需要)它必须存在。它应该是这样的:

import os

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.local")

# This application object is used by the development server
# as well as any WSGI server configured to use this file.
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()

现在,您可以拥有一个local.py可以配置所有者和组的模块,以便只有授权人员和Django进程才能读取文件的内容。

No, private keys and passwords do not fall under revision control. There is no reason to burden everyone with read access to your repository with knowing sensitive service credentials used in production, when most likely not all of them should have access to those services.

Starting with Django 1.4, your Django projects now ship with a project.wsgi module that defines the application object and it’s a perfect place to start enforcing the use of a project.local settings module that contains site-specific configurations.

This settings module is ignored from revision control, but it’s presence is required when running your project instance as a WSGI application, typical for production environments. This is how it should look like:

import os

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.local")

# This application object is used by the development server
# as well as any WSGI server configured to use this file.
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()

Now you can have a local.py module who’s owner and group can be configured so that only authorized personnel and the Django processes can read the file’s contents.


回答 13

如果您需要VCS作为秘密,则至少应将其保存在与实际代码分开的第二个存储库中。因此,您可以授予团队成员访问源代码存储库的权限,而他们不会看到您的凭据。此外,将此存储库托管在其他位置(例如,在具有加密文件系统的您自己的服务器上,而不是在github上),并将其检出到生产系统中,可以使用git-submodule之类的东西。

If you need VCS for your secrets you should at least keep them in a second repository seperated from you actual code. So you can give your team members access to the source code repository and they won’t see your credentials. Furthermore host this repository somewhere else (eg. on your own server with an encrypted filesystem, not on github) and for checking it out to the production system you could use something like git-submodule.


回答 14

另一种方法可能是完全避免在版本控制系统中保存机密,而是使用诸如hashicorp的Vault之类的工具,该工具是具有密钥滚动和审核,具有API和嵌入式加密功能的秘密存储。

Another approach could be to completely avoid saving secrets in version control systems and instead use a tool like vault from hashicorp, a secret storage with key rolling and auditing, with an API and embedded encryption.


回答 15

这是我的工作:

  • 将所有秘密保留为$ HOME / .bashrc来源的$ HOME / .secrets(go-r权限)中的env vars(这样,如果您在某人面前打开.bashrc,他们将看不到这些秘密)
  • 配置文件作为模板存储在VCS中,例如config.properties存储为config.properties.tmpl
  • 模板文件包含机密的占位符,例如:

    my.password = ## MY_PASSWORD ##

  • 在应用程序部署中,将运行脚本,该脚本将模板文件转换为目标文件,并用环境变量的值替换占位符,例如将## MY_PASSWORD ##更改为$ MY_PASSWORD的值。

This is what I do:

  • Keep all secrets as env vars in $HOME/.secrets (go-r perms) that $HOME/.bashrc sources (this way if you open .bashrc in front of someone, they won’t see the secrets)
  • Configuration files are stored in VCS as templates, such as config.properties stored as config.properties.tmpl
  • The template files contain a placeholder for the secret, such as:

    my.password=##MY_PASSWORD##

  • On application deployment, script is ran that transforms the template file into the target file, replacing placeholders with values of environment variables, such as changing ##MY_PASSWORD## to the value of $MY_PASSWORD.


回答 16

如果您的系统提供了此功能,则可以使用EncFS。因此,您可以将加密的数据保留为存储库的子文件夹,同时为应用程序提供对已装入数据的解密视图。由于加密是透明的,因此在拉或推上不需要任何特殊操作。

但是,它将需要挂载EncFS文件夹,这可以由您的应用程序根据存储在版本化文件夹之外的其他位置的密码(例如,环境变量)来完成。

You could use EncFS if your system provides that. Thus you could keep your encrypted data as a subfolder of your repository, while providing your application a decrypted view to the data mounted aside. As the encryption is transparent, no special operations are needed on pull or push.

It would however need to mount the EncFS folders, which could be done by your application based on an password stored elsewhere outside the versioned folders (eg. environment variables).


我应该在.gitignore文件中添加Django迁移文件吗?

问题:我应该在.gitignore文件中添加Django迁移文件吗?

我应该在文件中添加Django迁移.gitignore文件吗?

由于迁移冲突,我最近遇到了很多git问题,并且想知道是否应该将迁移文件标记为“忽略”。

如果是这样,我将如何添加我在应用程序中拥有的所有迁移并将它们添加到.gitignore文件中?

Should I be adding the Django migration files in the .gitignore file?

I’ve recently been getting a lot of git issues due to migration conflicts and was wondering if I should be marking migration files as ignore.

If so, how would I go about adding all of the migrations that I have in my apps, and adding them to the .gitignore file?


回答 0

引用Django迁移文档

每个应用程序的迁移文件都位于该应用程序内的“迁移”目录中,并被设计为提交至其代码库并作为其代码库的一部分进行分发。您应该在开发计算机上制作一次,然后在同事的计算机,登台计算机以及最终的生产计算机上运行相同的迁移。

如果遵循此过程,则迁移文件中不会出现任何合并冲突。

合并版本控制分支时,您仍然可能会遇到基于同一父级迁移的多个迁移的情况,例如,如果不同的开发人员同时引入了迁移。解决这种情况的一种方法是引入_merge_migration_。通常这可以通过以下命令自动完成

./manage.py makemigrations --merge

这将引入一个新的迁移,该迁移取决于当前的所有head迁移。当然,这仅在磁头迁移之间没有冲突时才有效,在这种情况下,您将必须手动解决问题。


鉴于这里有人建议您不要将迁移提交到版本控制,因此我想详细说明为什么您应该这样做。

首先,您需要记录应用于生产系统的迁移。如果将更改部署到生产中并想迁移数据库,则需要描述当前状态。您可以为应用到每个生产数据库的迁移创建单独的备份,但这似乎不必要。

其次,迁移通常包含自定义的手写代码。并非总是可以使用自动生成它们./manage.py makemigrations

第三,迁移应包含在代码审查中。它们是对您的生产系统的重大更改,很多事情都可能出错。

简而言之,如果您关心生产数据,请检查您向版本控制的迁移。

Quoting from the Django migrations documentation:

The migration files for each app live in a “migrations” directory inside of that app, and are designed to be committed to, and distributed as part of, its codebase. You should be making them once on your development machine and then running the same migrations on your colleagues’ machines, your staging machines, and eventually your production machines.

If you follow this process, you shouldn’t be getting any merge conflicts in the migration files.

When merging version control branches, you still may encounter a situation where you have multiple migrations based on the same parent migration, e.g. if to different developers introduced a migration concurrently. One way of resolving this situation is to introduce a _merge_migration_. Often this can be done automatically with the command

./manage.py makemigrations --merge

which will introduce a new migration that depends on all current head migrations. Of course this only works when there is no conflict between the head migrations, in which case you will have to resolve the problem manually.


Given that some people here suggested that you shouldn’t commit your migrations to version control, I’d like to expand on the reasons why you actually should do so.

First, you need a record of the migrations applied to your production systems. If you deploy changes to production and want to migrate the database, you need a description of the current state. You can create a separate backup of the migrations applied to each production database, but this seems unnecessarily cumbersome.

Second, migrations often contain custom, handwritten code. It’s not always possible to automatically generate them with ./manage.py makemigrations.

Third, migrations should be included in code review. They are significant changes to your production system, and there are lots of things that can go wrong with them.

So in short, if you care about your production data, please check your migrations into version control.


回答 1

您可以按照以下过程进行操作。

您可以在makemigrations本地运行,这将创建迁移文件。提交此新的迁移文件以回购。

我认为您根本不应该makemigrations投入生产。您可以migrate在生产环境中运行,并且会看到从您从本地提交的迁移文件中应用了迁移。这样您可以避免所有冲突。

在本地环境中,要创建迁移文件,

python manage.py makemigrations 
python manage.py migrate

现在提交这些新创建的文件,如下所示。

git add app/migrations/...
git commit -m 'add migration files' app/migrations/...

在生产环境中,仅运行以下命令。

python manage.py migrate

You can follow the below process.

You can run makemigrations locally and this creates the migration file. Commit this new migration file to repo.

In my opinion you should not run makemigrations in production at all. You can run migrate in production and you will see the migrations are applied from the migration file that you committed from local. This way you can avoid all conflicts.

IN LOCAL ENV, to create the migration files,

python manage.py makemigrations 
python manage.py migrate

Now commit these newly created files, something like below.

git add app/migrations/...
git commit -m 'add migration files' app/migrations/...

IN PRODUCTION ENV, run only the below command.

python manage.py migrate

回答 2

引用2018年文档Django 2.0。(两个单独的命令= makemigrationsmigrate

之所以有单独的命令来进行和应用迁移,是因为您会将迁移提交到版本控制系统,并随应用程序一起交付;它们不仅使您的开发更加容易,而且还可以被其他开发人员和生产环境使用。

https://docs.djangoproject.com/en/2.0/intro/tutorial02/

Quote from the 2018 docs, Django 2.0. (two separate commands = makemigrations and migrate)

The reason that there are separate commands to make and apply migrations is because you’ll commit migrations to your version control system and ship them with your app; they not only make your development easier, they’re also useable by other developers and in production.

https://docs.djangoproject.com/en/2.0/intro/tutorial02/


回答 3

TL; DR:提交迁移,解决迁移冲突,调整git工作流程。

感觉就像您需要调整git工作流程,而不是忽略冲突。

理想情况下,每个新功能都在不同的分支中开发,并与拉取请求合并回去。

如果存在冲突,则无法合并PR,因此需要合并其功能的人员需要解决冲突,包括迁移。这可能需要不同团队之间的协调。

提交迁移文件很重要!如果发生冲突,Django甚至可以帮助您解决那些冲突 ;)

TL;DR: commit migrations, resolve migration conflicts, adjust your git workflow.

Feels like you’d need to adjust your git workflow, instead of ignoring conflicts.

Ideally, every new feature is developed in a different branch, and merged back with a pull request.

PRs cannot be merged if there’s a conflict, therefore who needs to merge his feature needs to resolve the conflict, migrations included. This might need coordination between different teams.

It is important though to commit migration files! If a conflict arises, Django might even help you solve those conflicts ;)


回答 4

我无法想象为什么会出现冲突,除非您以某种方式编辑迁移?通常情况下,这很糟糕-如果有人错过了一些中间提交,那么他们就不会从正确的版本升级,并且他们的数据库副本也会被破坏。

我遵循的过程非常简单-每当您更改应用程序的模型时,您也会提交迁移,然后迁移不会改变 -如果您需要模型中的其他内容,则可以更改模型并提交新的迁移以及您的更改。

在未开发项目中,您通常可以删除迁移,并在发布时从0001_开始重新迁移,但是如果您具有生产代码,则不能(尽管可以将迁移压缩为一个)。

I can’t imagine why you would be getting conflicts, unless you’re editing the migrations somehow? That usually ends badly – if someone misses some intermediate commits then they won’t be upgrading from the correct version, and their copy of the database will be corrupted.

The process that I follow is pretty simple – whenever you change the models for an app, you also commit a migration, and then that migration doesn’t change – if you need something different in the model, then you change the model and commit a new migration alongside your changes.

In greenfield projects, you can often delete the migrations and start over from scratch with a 0001_ migration when you release, but if you have production code, then you can’t (though you can squash migrations down into one).


回答 5

通常使用的解决方案是,在将任何东西合并到母版之前,开发人员必须拉动任何远程更改。如果迁移版本存在冲突,则他应将其本地迁移(远程迁移已由其他开发人员运行,并且有可能在生产环境中)重命名为N + 1。

在开发过程中,不提交迁移就可以了(尽管不要添加忽略,只是不要添加忽略add)。但是一旦投入生产,就需要使用它们,以使模式与模型更改保持同步。

然后,您需要编辑文件,并将其更改dependencies为最新的远程版本。

这适用于Django迁移以及其他类似应用程序(sqlalchemy + alembic,RoR等)。

The solution usually used, is that, before anything is merged into master, the developer must pull any remote changes. If there’s a conflict in migration versions, he should rename his local migration (the remote one has been run by other devs, and, potentially, in production), to N+1.

During development it might be okay to just not-commit migrations (don’t add an ignore though, just don’t add them). But once you’ve gone into production, you’ll need them in order to keep the schema in sync with model changes.

You then need to edit the file, and change the dependencies to the latest remote version.

This works for Django migrations, as well as other similar apps (sqlalchemy+alembic, RoR, etc).


回答 6

在git中有一堆迁移文件很麻烦。迁移文件夹中只有一个文件,您不应忽略。该文件是init .py文件,如果忽略它,python将不再在目录内寻找子模块,因此任何导入模块的尝试都会失败。所以问题应该怎么忽略所有迁移文件,但初始化的.py?解决方案是:将’0 * .py’添加到.gitignore文件中,即可完美完成工作。

希望这对某人有帮助。

Having a bunch of migration files in git is messy. There is only one file in migration folder that you should not ignore. That file is init.py file, If you ignore it, python will no longer look for submodules inside the directory, so any attempts to import the modules will fail. So the question should be how to ignore all migration files but init.py? The solution is: Add ‘0*.py’ to .gitignore files and it does the job perfectly.

Hope this helps someone.


回答 7

如果您具有用于开发,登台和生产环境的单独的数据库,则忽略迁移。对于开发人员。目的您可以使用本地sqlite DB并在本地进行迁移。我建议您另外创建四个分支:

  1. 主-清除新代码而不进行迁移。没有人连接到该分支。仅用于代码审查

  2. 开发-日常开发。接受推/拉。每个开发人员都在使用sqlite DB

  3. Cloud_DEV_env-远程云/服务器DEV环境。只拉。将迁移保留在本地计算机上,用于代码部署和Dev数据库的远程迁移

  4. Cloud_STAG_env-远程云/服务器STAG环境。只拉。将迁移保留在本地计算机上,该迁移用于Stag数据库的代码部署和远程迁移

  5. Cloud_PROD_env-远程云/服务器DEV环境。只拉。将迁移保留在本地计算机上,该迁移用于Prod数据库的代码部署和远程迁移

注意:2、3、4-迁移可以保存在存储库中,但是应该有合并合并拉取请求的严格规则,因此我们决定找一个负责部署的人员,所以唯一拥有所有迁移文件的人-我们的部署-嗯 每当我们对模型进行任何更改时,他都会保留远程数据库迁移。

Gitignore the migrations, if You have separate DBs for Development, Staging and Production environment. For dev. purposes You can use local sqlite DB and play with migrations locally. I would recommend You to create four additional branches:

  1. Master – Clean fresh code without migrations. Nobody is connected to this branch. Used for code reviews only

  2. Development – daily development. Push/pull accepted. Each developer is working on sqlite DB

  3. Cloud_DEV_env – remote cloud/server DEV environment. Pull only. Keep migrations locally on machine, which is used for the code deployment and remote migrations of Dev database

  4. Cloud_STAG_env – remote cloud/server STAG environment. Pull only. Keep migrations locally on machine, which is used for the code deployment and remote migrations of Stag database

  5. Cloud_PROD_env – remote cloud/server DEV environment. Pull only. Keep migrations locally on machine, which is used for the code deployment and remote migrations of Prod database

Notes: 2, 3, 4 – migrations can be kept in repos but there should be strict rules of pull requests merging, so we decided to find a person, responsible for deployments, so the only guy who has all the migration files – our deploy-er. He keeps the remote DB migrations each time we have any changes in Models.


回答 8

简短答案 我建议排除回购中的迁移。代码合并后,只需运行即可./manage.py makemigrations

长答案 我不认为您应该将迁移文件放入存储库中。它将破坏其他人的开发环境以及其他产品和阶段环境中的迁移状态。(有关示例,请参见Sugar Tang的评论)。

以我的观点,Django迁移的目的是找到先前模型状态和新模型状态之间的差距,然后序列化该差距。如果您的模型在代码合并后发生更改,则可以简单makemigrations地找出差距。当您可以自动实现相同且无错误的迁移时,为什么要手动仔细合并其他迁移?Django文档说,

它们*(迁移)*被设计为自动的

; 请保持这种方式。要手动合并迁移,您必须完全了解其他更改和更改的任何依存关系。这会产生很多开销,而且容易出错。因此,跟踪模型文件就足够了。

这是工作流程中的一个好话题。我愿意接受其他选择。

Short answer I propose excluding migrations in the repo. After code merge, just run ./manage.py makemigrations and you are all set.

Long answer I don’t think you should put migrations files into repo. It will spoil the migration states in other person’s dev environment and other prod and stage environment. (refer to Sugar Tang’s comment for examples).

In my point of view, the purpose of Django migrations is to find gaps between previous model states and new model states, and then serialise the gap. If your model changes after code merge, you can simple do makemigrations to find out the gap. Why do you want to manually and carefully merge other migrations when you can achieve the same automatically and bug free? Django documentation says,

They*(migrations)*’re designed to be mostly automatic

; please keep it that way. To merge migrations manually, you have to fully understand what others have changed and any dependence of the changes. That’s a lot of overhead and error prone. So tracking models file is sufficient.

It is a good topic on the workflow. I am open to other options.


忽略git存储库中的.pyc文件

问题:忽略git存储库中的.pyc文件

如何忽略.pycgit中的文件?

如果我把它放在.gitignore里面是行不通的。我需要将它们取消跟踪,而不要检查提交。

How can I ignore .pyc files in git?

If I put it in .gitignore it doesn’t work. I need them to be untracked and not checked for commits.


回答 0

放进去.gitignore。但是从gitignore(5)手册页:

  ·   If the pattern does not contain a slash /, git treats it as a shell
       glob pattern and checks for a match against the pathname relative
       to the location of the .gitignore file (relative to the toplevel of
       the work tree if not from a .gitignore file).

  ·   Otherwise, git treats the pattern as a shell glob suitable for
       consumption by fnmatch(3) with the FNM_PATHNAME flag: wildcards in
       the pattern will not match a / in the pathname. For example,
       "Documentation/*.html" matches "Documentation/git.html" but not
       "Documentation/ppc/ppc.html" or
       "tools/perf/Documentation/perf.html".

因此,要么指定适当*.pyc条目的完整路径,要么将其放置在.gitignore从存储库根目录开始(包含在内)的任何目录中的文件中。

Put it in .gitignore. But from the gitignore(5) man page:

  ·   If the pattern does not contain a slash /, git treats it as a shell
       glob pattern and checks for a match against the pathname relative
       to the location of the .gitignore file (relative to the toplevel of
       the work tree if not from a .gitignore file).

  ·   Otherwise, git treats the pattern as a shell glob suitable for
       consumption by fnmatch(3) with the FNM_PATHNAME flag: wildcards in
       the pattern will not match a / in the pathname. For example,
       "Documentation/*.html" matches "Documentation/git.html" but not
       "Documentation/ppc/ppc.html" or
       "tools/perf/Documentation/perf.html".

So, either specify the full path to the appropriate *.pyc entry, or put it in a .gitignore file in any of the directories leading from the repository root (inclusive).


回答 1

您应该添加以下行:

*.pyc 

.gitignore库初始化之后就在你的git仓库树的根文件夹中的文件。

正如ralphtheninja所说,如果您忘记事先做,只要将行添加到.gitignore文件中,所有先前提交的.pyc文件仍将被跟踪,因此您需要将它们从存储库中删除。

如果您使用的是Linux系统(或MacOSX等“父级”),则只需使用以下一条从存储库根目录执行的命令即可快速完成此操作:

find . -name "*.pyc" -exec git rm -f "{}" \;

这只是意味着:

从我当前所在的目录开始,查找名称以extension结尾的所有文件.pyc,并将文件名传递给命令git rm -f

之后*.pyc从混帐作为跟踪文件的文件删除,提交此变更到仓库,然后你就可以在最后加*.pyc一行到.gitignore文件中。

(改编自http://yuji.wordpress.com/2010/10/29/git-remove-all-pyc/

You should add a line with:

*.pyc 

to the .gitignore file in the root folder of your git repository tree right after repository initialization.

As ralphtheninja said, if you forgot to to do it beforehand, if you just add the line to the .gitignore file, all previously committed .pyc files will still be tracked, so you’ll need to remove them from the repository.

If you are on a Linux system (or “parents&sons” like a MacOSX), you can quickly do it with just this one line command that you need to execute from the root of the repository:

find . -name "*.pyc" -exec git rm -f "{}" \;

This just means:

starting from the directory i’m currently in, find all files whose name ends with extension .pyc, and pass file name to the command git rm -f

After *.pyc files deletion from git as tracked files, commit this change to the repository, and then you can finally add the *.pyc line to the .gitignore file.

(adapted from http://yuji.wordpress.com/2010/10/29/git-remove-all-pyc/)


回答 2

在放入之前,您可能已将它们添加到存储库*.pyc.gitignore
首先从存储库中删除它们。

You have probably added them to the repository before putting *.pyc in .gitignore.
First remove them from the repository.


回答 3

我尝试使用先前文章的句子,并且不递归工作,然后阅读一些帮助并获得以下内容:

find . -name "*.pyc" -exec git rm -f "{}" \;

要在.gitignore文件中添加* .pyc以保持git干净,必须使用pd

echo "*.pyc" >> .gitignore

请享用。

i try to use the sentence of a prior post and don’t work recursively, then read some help and get this line:

find . -name "*.pyc" -exec git rm -f "{}" \;

p.d. is necessary to add *.pyc in .gitignore file to maintain git clean

echo "*.pyc" >> .gitignore

Enjoy.


回答 4

感谢@Enrico的答案。

请注意,如果您使用的是virtualenv,则.pyc您当前所在的目录中还会有几个文件,这些文件将由他的find命令捕获。

例如:

./app.pyc
./lib/python2.7/_weakrefset.pyc
./lib/python2.7/abc.pyc
./lib/python2.7/codecs.pyc
./lib/python2.7/copy_reg.pyc
./lib/python2.7/site-packages/alembic/__init__.pyc
./lib/python2.7/site-packages/alembic/autogenerate/__init__.pyc
./lib/python2.7/site-packages/alembic/autogenerate/api.pyc

我想删除所有文件是没有害处的,但是如果您只想删除.pyc主目录中的文件,则只需执行

find "*.pyc" -exec git rm -f "{}" \;

这将仅从app.pycgit存储库中删除文件。

Thanks @Enrico for the answer.

Note if you’re using virtualenv you will have several more .pyc files within the directory you’re currently in, which will be captured by his find command.

For example:

./app.pyc
./lib/python2.7/_weakrefset.pyc
./lib/python2.7/abc.pyc
./lib/python2.7/codecs.pyc
./lib/python2.7/copy_reg.pyc
./lib/python2.7/site-packages/alembic/__init__.pyc
./lib/python2.7/site-packages/alembic/autogenerate/__init__.pyc
./lib/python2.7/site-packages/alembic/autogenerate/api.pyc

I suppose it’s harmless to remove all the files, but if you only want to remove the .pyc files in your main directory, then just do

find "*.pyc" -exec git rm -f "{}" \;

This will remove just the app.pyc file from the git repository.


如何使用pip从git子目录安装?

问题:如何使用pip从git子目录安装?

我有一个带有许多文件夹的git存储库,其中一个是可通过pip安装的python模块,如下所示:

repo.git/
repo.git/folder1/
repo.git/folder2/
repo.git/mymodule/
repo.git/mymodule/__init__.py
repo.git/mymodule/setup.py
repo.git/mymodule/...

现在,我必须执行以下操作才能安装:

git clone http://server/repo.git
cd repo
pip install mymodule
cd ..
rm -rf repo

是否可以通过pip直接安装模块而无需显式克隆?

我试过了:

pip install git+https://server/repo.git/mymodule/
pip install git+https://server/repo.git:mymodule/

但是我得到:

IOError: [Errno 2] No such file or directory: '/tmp/pip-88tlLm-build/setup.py'

I have a git repository with many folders, one of them being a python module installable with pip, like this:

repo.git/
repo.git/folder1/
repo.git/folder2/
repo.git/mymodule/
repo.git/mymodule/__init__.py
repo.git/mymodule/setup.py
repo.git/mymodule/...

Right now I have to do the following to install:

git clone http://server/repo.git
cd repo
pip install mymodule
cd ..
rm -rf repo

Is it possible to install the module directly with pip without explicitly cloning ?

I tried:

pip install git+https://server/repo.git/mymodule/
pip install git+https://server/repo.git:mymodule/

But I get:

IOError: [Errno 2] No such file or directory: '/tmp/pip-88tlLm-build/setup.py'

回答 0

有一个关于此功能的请求请求,它似乎已经在一个月前合并到了开发分支中。语法如下

pip install -e git+https://git.repo/some_repo.git#egg=version_subpkg&subdirectory=repo # install a python package from a repo subdirectory

我们可能要等一会儿,直到它合并到master并分发。

更新:现在可以在https://pip.readthedocs.io/en/stable/reference/pip_install/#vcs-support上获得和记录,如下所示:

对于setup.py不在项目根目录中的项目,将使用“子目录”组件。“子目录”组件的值应该是从项目根目录到setup.py所在位置的路径。

因此,如果您的存储库布局为:

- pkg_dir/
  - setup.py  # setup.py for package ``pkg``
  - some_module.py
- other_dir/
  - some_file
- some_other_file

您需要使用

pip install -e vcs+protocol://repo_url/#egg=pkg&subdirectory=pkg_dir

注意:在Windows上,必须将URL用双引号引起来,否则会出现错误“无法将’子目录’识别为内部或外部命令”。例如,使用:

pip install -e "vcs+protocol://repo_url#egg=pkg&subdirectory=pkg_dir"

There is a pull request regarding this feature, and it seems to have been merged to develop branch a month ago. The syntax is the following:

pip install -e git+https://git.repo/some_repo.git#egg=version_subpkg&subdirectory=repo # install a python package from a repo subdirectory

We probably have to wait for a while until it gets merged to master and is distributed.

UPDATE: This is now available and documented at https://pip.readthedocs.io/en/stable/reference/pip_install/#vcs-support as follows:

For projects where setup.py is not in the root of project, “subdirectory” component is used. Value of “subdirectory” component should be a path starting from root of the project to where setup.py is located.

So if your repository layout is:

- pkg_dir/
  - setup.py  # setup.py for package ``pkg``
  - some_module.py
- other_dir/
  - some_file
- some_other_file

You’ll need to use

pip install -e vcs+protocol://repo_url/#egg=pkg&subdirectory=pkg_dir

Note: On Windows, you must place the URL in double quotes, or you’ll get an error “‘subdirectory’ is not recognized as an internal or external command”. E.g., use:

pip install -e "vcs+protocol://repo_url#egg=pkg&subdirectory=pkg_dir"

回答 1

已经在正确答案下的注释之一中指出了这一点,但只是为了强调此问题:从Linux命令行执行此命令时,必须逃脱&-character,因为&符告诉命令行在后台运行命令:

git+https://git.repo/some_repo.git#egg=version_subpkg\&subdirectory=repo

注意在“&”号之前的反斜杠。转义行为可能取决于Linux发行版。我不是专家。
如果您忽略此设置,则可能会遇到类似以下的神秘错误:

bash: (...) command not found

It’s been already stated in one of the comments under the correct answer, but just to highlight this issue: when executing this from Linux command line, you must escape the &-character since ampersand is telling the command line to run a command in background:

git+https://git.repo/some_repo.git#egg=version_subpkg\&subdirectory=repo

Notice the backslash before the ampersand. The escaping behaviour might depend on the Linux distro; I’m not an expert.
If you ignore this, you might run into a cryptic error like the following:

bash: (...) command not found

如何编写setup.py以包含Git存储库作为依赖项

问题:如何编写setup.py以包含Git存储库作为依赖项

我正在尝试setup.py为我的包裹写东西。我的软件包需要指定对另一个Git存储库的依赖。

这是我到目前为止所拥有的:

from setuptools import setup, find_packages

setup(
    name='abc',
    packages=find_packages(),
    url='https://github.abc.com/abc/myabc',
    description='This is a description for abc',
    long_description=open('README.md').read(),
    install_requires=[
        "requests==2.7.0",
        "SomePrivateLib>=0.1.0",
        ],
    dependency_links = [
     "git+git://github.abc.com/abc/SomePrivateLib.git#egg=SomePrivateLib",
    ],
    include_package_data=True,
)

当我跑步时:

pip install -e https://github.abc.com/abc/myabc.git#egg=analyse

我懂了

找不到满足SomePrivateLib> = 0.1.0要求的版本(来自分析)(来自版本:)找不到SomePrivateLib> = 0.1.0(来自分析)的匹配分布

我究竟做错了什么?

I am trying to write setup.py for my package. My package needs to specify a dependency on another git repo.

This is what I have so far:

from setuptools import setup, find_packages

setup(
    name='abc',
    packages=find_packages(),
    url='https://github.abc.com/abc/myabc',
    description='This is a description for abc',
    long_description=open('README.md').read(),
    install_requires=[
        "requests==2.7.0",
        "SomePrivateLib>=0.1.0",
        ],
    dependency_links = [
     "git+git://github.abc.com/abc/SomePrivateLib.git#egg=SomePrivateLib",
    ],
    include_package_data=True,
)

When I run:

pip install -e https://github.abc.com/abc/myabc.git#egg=analyse

I get

Could not find a version that satisfies the requirement SomePrivateLib>=0.1.0 (from analyse) (from versions: ) No matching distribution found for SomePrivateLib>=0.1.0 (from analyse)

What am I doing wrong ?


回答 0

您可以在此处找到正确的方法。

dependency_links=['http://github.com/user/repo/tarball/master#egg=package-1.0']

关键不是提供指向Git存储库的链接,而是提供指向tarball的链接。如果您/tarball/master像上面那样添加,GitHub会为您创建master分支的压缩包。

You can find the right way to do it here.

dependency_links=['http://github.com/user/repo/tarball/master#egg=package-1.0']

The key is not to give a link to a git repository, but a link to a tarball. Github creates a tarball of the master branch for you if you append /tarball/master as shown above.


回答 1

在通过上面的评论中@muon链接的pip问题3939PEP-508规范后,我发现成功地通过setup.py使用以下规范模式来安装我的私有repo依赖项install_requires(不再dependency_links):

install_requires = [
  'some-pkg @ git+ssh://git@github.com/someorgname/pkg-repo-name@v1.1#egg=some-pkg',
]

@v1.1指示在GitHub上创建的发布标志,并可以用一个分支,提交,或不同类型的标签来代替。

After digging through the pip issue 3939 linked by @muon in the comments above and then the PEP-508 specification, I found success getting my private repo dependency to install via setup.py using this specification pattern in install_requires (no more dependency_links):

install_requires = [
  'some-pkg @ git+ssh://git@github.com/someorgname/pkg-repo-name@v1.1#egg=some-pkg',
]

The @v1.1 indicates the release tag created on github and could be replaced with a branch, commit, or different type of tag.


回答 2

以下答案不适用于Pip 19+


不幸的是,其他答案不适用于私有存储库,这是最常见的用例之一。我最终的确使用了如下setup.py文件:

from setuptools import setup, find_packages

setup(
    name = 'MyProject',
    version = '0.1.0',
    url = '',
    description = '',
    packages = find_packages(),
    install_requires = [
        # Github Private Repository - needs entry in `dependency_links`
        'ExampleRepo'
    ],

    dependency_links=[
        # Make sure to include the `#egg` portion so the `install_requires` recognizes the package
        'git+ssh://git@github.com/example_organization/ExampleRepo.git#egg=ExampleRepo-0.1'
    ]
)

较新版本的pip消除了使用“ dependency_links”的需要,从而使操作更加轻松-

from setuptools import setup, find_packages

setup(
    name = 'MyProject',
    version = '0.1.0',
    url = '',
    description = '',
    packages = find_packages(),
    install_requires = [
        # Github Private Repository
        'ExampleRepo @ git+ssh://git@github.com/example_organization/ExampleRepo.git#egg=ExampleRepo-0.1'
    ]
)

The following answer is deprecated for Pip 19+


Unfortunately the other answer does not work with private repositories, which is one of the most common use cases for this. I eventually did get it working with a setup.py file that looks like this:

from setuptools import setup, find_packages

setup(
    name = 'MyProject',
    version = '0.1.0',
    url = '',
    description = '',
    packages = find_packages(),
    install_requires = [
        # Github Private Repository - needs entry in `dependency_links`
        'ExampleRepo'
    ],

    dependency_links=[
        # Make sure to include the `#egg` portion so the `install_requires` recognizes the package
        'git+ssh://git@github.com/example_organization/ExampleRepo.git#egg=ExampleRepo-0.1'
    ]
)

Newer versions of pip make this even easier by removing the need to use “dependency_links”-

from setuptools import setup, find_packages

setup(
    name = 'MyProject',
    version = '0.1.0',
    url = '',
    description = '',
    packages = find_packages(),
    install_requires = [
        # Github Private Repository
        'ExampleRepo @ git+ssh://git@github.com/example_organization/ExampleRepo.git#egg=ExampleRepo-0.1'
    ]
)

回答 3

一个更一般的答案:要从requirements.txt文件中获取信息,请执行以下操作:

from setuptools import setup, find_packages
from os import path

loc = path.abspath(path.dirname(__file__))

with open(loc + '/requirements.txt') as f:
    requirements = f.read().splitlines()

required = []
dependency_links = []

# Do not add to required lines pointing to Git repositories
EGG_MARK = '#egg='
for line in requirements:
    if line.startswith('-e git:') or line.startswith('-e git+') or \
            line.startswith('git:') or line.startswith('git+'):
        if EGG_MARK in line:
            package_name = line[line.find(EGG_MARK) + len(EGG_MARK):]
            required.append(package_name)
            dependency_links.append(line)
        else:
            print('Dependency to a git repository should have the format:')
            print('git+ssh://git@github.com/xxxxx/xxxxxx#egg=package_name')
    else:
        required.append(line)

setup(
    name='myproject',  # Required
    version='0.0.1',  # Required
    description='Description here....',  # Required
    packages=find_packages(),  # Required
    install_requires=required,
    dependency_links=dependency_links,
)

A more general answer, to get the information from the requeriments.txt file I do:

from setuptools import setup, find_packages
from os import path

loc = path.abspath(path.dirname(__file__))

with open(loc + '/requirements.txt') as f:
    requirements = f.read().splitlines()

required = []
dependency_links = []
# do not add to required lines pointing to git repositories
EGG_MARK = '#egg='
for line in requirements:
    if line.startswith('-e git:') or line.startswith('-e git+') or \
            line.startswith('git:') or line.startswith('git+'):
        if EGG_MARK in line:
            package_name = line[line.find(EGG_MARK) + len(EGG_MARK):]
            required.append(package_name)
            dependency_links.append(line)
        else:
            print('Dependency to a git repository should have the format:')
            print('git+ssh://git@github.com/xxxxx/xxxxxx#egg=package_name')
    else:
        required.append(line)

setup(
    name='myproject',  # Required
    version='0.0.1',  # Required
    description='Description here....',  # Required
    packages=find_packages(),  # Required
    install_requires=required,
    dependency_links=dependency_links,
) 

回答 4

实际上,如果您希望递归安装软件包(YourCurrentPackage包含SomePrivateLib),例如,当您希望将YourCurrentPackage包含到另一个软件包中(例如OuterPackage→YourCurrentPackage→SomePrivateLib)时,您将同时需要:

install_requires=[
    ...,
    "SomePrivateLib @ git+ssh://github.abc.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
],
dependency_links = [
    "git+ssh://github.abc.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
]

并确保您使用版本号创建了一个标签。

同样,如果您的Git项目是私有的,并且您想将其安装在容器中(例如DockerGitLab运行器),那么您将需要对存储库的授权访问。请考虑将Git + HTTPS与访问令牌一起使用(例如在GitLab上:https : //docs.gitlab.com/ee/user/profile/personal_access_tokens.html ):

import os
from setuptools import setup

TOKEN_VALUE = os.getenv('EXPORTED_VAR_WITH_TOKEN')

setup(
    ....

    install_requires=[
            ...,
            f"SomePrivateLib @ git+https://gitlab-ci-token:{TOKEN_VALUE}@gitlab.server.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
    ],
    dependency_links = [
            f"git+https://gitlab-ci-token:{TOKEN_VALUE}@gitlab.server.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
    ]
)

Actually if you like to make your packages installable recursivelly (YourCurrentPackage includes your SomePrivateLib), e.g. when you want to include YourCurrentPackage into another one (like OuterPackage -> YourCurrentPackage -> SomePrivateLib) you’ll need both:

install_requires=[
    ...,
    "SomePrivateLib @ git+ssh://github.abc.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
],
dependency_links = [
    "git+ssh://github.abc.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
]

And make sure you have a tag created with your version number.

Also if your git project is private and you like to install it inside the container e.g. Docker or GitLab runner, you will need authorized access to your repo. Please consider to use git+https with access tokens (like on GitLab: https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html):

import os
from setuptools import setup

TOKEN_VALUE = os.getenv('EXPORTED_VAR_WITH_TOKEN')

setup(
    ....

    install_requires=[
            ...,
            f"SomePrivateLib @ git+https://gitlab-ci-token:{TOKEN_VALUE}@gitlab.server.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
    ],
    dependency_links = [
            f"git+https://gitlab-ci-token:{TOKEN_VALUE}@gitlab.server.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
    ]
)

回答 5

我在GitLab中成功使用了这三个选项。我正在使用GitLab版本11。

选项1-未指定令牌。Shell将提示输入用户名/密码。

from setuptools import setup

TOKEN_VALUE = os.getenv('EXPORTED_VAR_WITH_TOKEN')

setup(
    install_requires=[
        "SomePrivateLib @ git+https://gitlab.server.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
    ]
)

选项2-指定了用户访问令牌。通过转到GitLab→帐户右上方→设置→访问令牌生成的令牌。创建具有read_repository权限的令牌。

例:

import os
from setuptools import setup

TOKEN_VALUE = os.getenv('EXPORTED_VAR_WITH_TOKEN')

setup(
    install_requires=[
        f"SomePrivateLib @ git+https://gitlab-ci-token:{TOKEN_VALUE}@gitlab.server.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
    ]
)

选项3-指定存储库级别的令牌。通过转到存储库→设置→存储库→部署令牌生成的令牌。在这里,创建一个具有read_repository权限的令牌。

例:

import os
from setuptools import setup

TOKEN_USER = os.getenv('EXPORTED_TOKEN_USER')
TOKEN_VALUE = os.getenv('EXPORTED_VAR_WITH_TOKEN')

setup(
    install_requires=[
        f"SomePrivateLib @ git+https://{TOKEN_USER}:{TOKEN_VALUE}@gitlab.server.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
    ]
)

在这三者中,我都可以简单地做:“ SomePrivateLib @ git + https://gitlab.server.com/abc/SomePrivateLib.git”,最后没有#egg标记。

I was successful with these 3 options in gitlab. I am using version 11 of gitlab.

option 1 – no token specified. shell will prompt for username/password.

from setuptools import setup

TOKEN_VALUE = os.getenv('EXPORTED_VAR_WITH_TOKEN')

setup(
    install_requires=[
        "SomePrivateLib @ git+https://gitlab.server.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
    ]
)

option 2 – user access token specified. token generated by going to gitlab > account top right > settings > access tokens. create token with read_repository rights.

example:

import os
from setuptools import setup

TOKEN_VALUE = os.getenv('EXPORTED_VAR_WITH_TOKEN')

setup(
    install_requires=[
        f"SomePrivateLib @ git+https://gitlab-ci-token:{TOKEN_VALUE}@gitlab.server.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
    ]
)

option 3 – repository-level token specified. token generated by going to the repository > settings > repository > deploy tokens. from here create a token with read_repository rights.

example:

import os
from setuptools import setup

TOKEN_USER = os.getenv('EXPORTED_TOKEN_USER')
TOKEN_VALUE = os.getenv('EXPORTED_VAR_WITH_TOKEN')

setup(
    install_requires=[
        f"SomePrivateLib @ git+https://{TOKEN_USER}:{TOKEN_VALUE}@gitlab.server.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
    ]
)

In all 3, I was able to do simply: “SomePrivateLib @ git+https://gitlab.server.com/abc/SomePrivateLib.git” without the #egg marking at the end.


Walle-web-Walle-瓦力DevOps开源项目代码部署平台

Walle 2.0-官方主页


字节跳动内推书名:Tob Lark招聘大数据研发、数据分析师,机会极佳,请勿错过.请各位朋友扩散下有需要的同学直达内推链接、帮助内推,君子成人之美,谢谢.

功能强大,且免费开源的walle-web 瓦力终于更新2.0.0了!

Walle让用户代码发布终于可以不只能选择Jenkins!支持各种Web代码发布,php、java、python、go等代码的发布、回滚可以通过Web来一键完成。Walle一个可自由配置项目,更人性化,高颜值,支持Git、多用户、多语言、多项目、多环境同时部署的开源上线部署系统.

2.0.0占用了我几乎所有业余时间,精力与金钱付出换各位使用收益,望各位喜欢不吝顺手star以示支持,项目更好亦反馈予你.目前2.0.0已经发布,请保持关注,我会在公众号更新(在最下面)。

有推广资源(开源文章推荐、大会分享)的同学,请微信联系我,强烈需要帮助.另外,老版本已迁移到walle 1.x的同学务必不要再更新了,两个版本不兼容

功能

  • gitlabRESTful API,类gitlab的权限模型.将来打通gitlab,良心的惊喜
  • 空间管理.意味着有独立的空间资源:环境管理、用户组、项目、服务器等
  • 灰度发布.呼声不断,终于来了
  • 项目管理.Deploy、Release的前置及后置Hook,自定义全局变量;自带检测、复制功能,都贴心到这种程度了
  • websocket实时展示部署中的shell console、跟真的终端长得一样.
  • 完善的通知机制.邮件、钉钉
  • 全新的UI、我自己都被震撼到了,如丝般流畅

架构


预览





安装

快速安装|安装错误|常见错误排解

路线图

  • 预览版2018/12/02
    • 安装文档、前后端代码、数据迁移
  • Alpha2018-12-09
    • 使用文档、故障排除、公众号更新
  • 测试版2018/12/23🎅圣诞夜前夕
    • 钉钉/邮件消息通知
    • 接受官网徽标企业的Trouble Shooting
  • 2.0.02018/12/301个️⃣元旦前夕
    • 项目检测、复制
    • 任务的回滚
    • released tag、使用文档
    • Docker镜像
    • JAVA配置模板
    • php配置模板
    • github5000star
  • 2.0.12019-01-13
    • 项目配置添加自定义变量
    • Python3.7+兼容
  • 2.1.02019-03-22
    • 超管权限完善
    • Dashboard1.0(全新的玩法,欢迎提问题)
    • 3月24日开源中国苏州源创会-开源综合技术主题“开源构建多空间可视化一键部署DevOps平台”
  • 2.2.02019-04-22
    • 网络挂钩(GitLab)
    • 上线时间记录、命令与结果拆分、实时控制台
    • 宿主机资源监控
  • 2.3.02019-05-27
    • 插件化:Maven、npm
    • 管道式
  • 2.4.02019-06-17
    • I18N国际化
  • 2.5.02019/07/29
    • 上线单差异
    • 消息通知定制化:钉钉、邮件、企业微信
  • 2.6.02019-08-19
    • 批量管理服务器
    • 跨空间复制项目
    • 应用程序打包平台
    • Dashboard2.0

在Python脚本中获取当前的git hash

问题:在Python脚本中获取当前的git hash

我想在Python脚本的输出中包括当前的git哈希值(作为生成该输出的代码的版本号)。

如何在Python脚本中访问当前的git哈希?

I would like to include the current git hash in the output of a Python script (as a the version number of the code that generated that output).

How can I access the current git hash in my Python script?


回答 0

git describe命令是创建代码的人为表示的“版本号”的好方法。从文档中的示例中:

使用git.git之类的当前树,我得到:

[torvalds@g5 git]$ git describe parent
v1.0.4-14-g2414721

即我的“父”分支的当前头基于v1.0.4,但是由于它的顶部还有一些提交,describe添加了额外的提交数量(“ 14”)和该提交的缩写对象名称本身(“ 2414721”)结尾。

在Python中,您可以执行以下操作:

import subprocess
label = subprocess.check_output(["git", "describe"]).strip()

The git describe command is a good way of creating a human-presentable “version number” of the code. From the examples in the documentation:

With something like git.git current tree, I get:

[torvalds@g5 git]$ git describe parent
v1.0.4-14-g2414721

i.e. the current head of my “parent” branch is based on v1.0.4, but since it has a few commits on top of that, describe has added the number of additional commits (“14”) and an abbreviated object name for the commit itself (“2414721”) at the end.

From within Python, you can do something like the following:

import subprocess
label = subprocess.check_output(["git", "describe"]).strip()

回答 1

无需费力地git自己从命令中获取数据。GitPython是执行此操作以及许多其他工作的非常好方法git。它甚至对Windows具有“尽力而为”的支持。

之后pip install gitpython,你可以做

import git
repo = git.Repo(search_parent_directories=True)
sha = repo.head.object.hexsha

No need to hack around getting data from the git command yourself. GitPython is a very nice way to do this and a lot of other git stuff. It even has “best effort” support for Windows.

After pip install gitpython you can do

import git
repo = git.Repo(search_parent_directories=True)
sha = repo.head.object.hexsha

Something to consider when using this library. The following is taken from gitpython.readthedocs.io

Leakage of System Resources

GitPython is not suited for long-running processes (like daemons) as it tends to leak system resources. It was written in a time where destructors (as implemented in the __del__ method) still ran deterministically.

In case you still want to use it in such a context, you will want to search the codebase for __del__ implementations and call these yourself when you see fit.

Another way assure proper cleanup of resources is to factor out GitPython into a separate process which can be dropped periodically


回答 2

这篇文章包含命令,Greg的答案包含subprocess命令。

import subprocess

def get_git_revision_hash():
    return subprocess.check_output(['git', 'rev-parse', 'HEAD'])

def get_git_revision_short_hash():
    return subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD'])

This post contains the command, Greg’s answer contains the subprocess command.

import subprocess

def get_git_revision_hash():
    return subprocess.check_output(['git', 'rev-parse', 'HEAD'])

def get_git_revision_short_hash():
    return subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD'])

回答 3

numpy有一个好看的多平台程序在其setup.py

import os
import subprocess

# Return the git revision as a string
def git_version():
    def _minimal_ext_cmd(cmd):
        # construct minimal environment
        env = {}
        for k in ['SYSTEMROOT', 'PATH']:
            v = os.environ.get(k)
            if v is not None:
                env[k] = v
        # LANGUAGE is used on win32
        env['LANGUAGE'] = 'C'
        env['LANG'] = 'C'
        env['LC_ALL'] = 'C'
        out = subprocess.Popen(cmd, stdout = subprocess.PIPE, env=env).communicate()[0]
        return out

    try:
        out = _minimal_ext_cmd(['git', 'rev-parse', 'HEAD'])
        GIT_REVISION = out.strip().decode('ascii')
    except OSError:
        GIT_REVISION = "Unknown"

    return GIT_REVISION

numpy has a nice looking multi-platform routine in its setup.py:

import os
import subprocess

# Return the git revision as a string
def git_version():
    def _minimal_ext_cmd(cmd):
        # construct minimal environment
        env = {}
        for k in ['SYSTEMROOT', 'PATH']:
            v = os.environ.get(k)
            if v is not None:
                env[k] = v
        # LANGUAGE is used on win32
        env['LANGUAGE'] = 'C'
        env['LANG'] = 'C'
        env['LC_ALL'] = 'C'
        out = subprocess.Popen(cmd, stdout = subprocess.PIPE, env=env).communicate()[0]
        return out

    try:
        out = _minimal_ext_cmd(['git', 'rev-parse', 'HEAD'])
        GIT_REVISION = out.strip().decode('ascii')
    except OSError:
        GIT_REVISION = "Unknown"

    return GIT_REVISION

回答 4

如果子进程不是可移植的,并且您不想安装软件包来执行此简单操作,则也可以执行此操作。

import pathlib

def get_git_revision(base_path):
    git_dir = pathlib.Path(base_path) / '.git'
    with (git_dir / 'HEAD').open('r') as head:
        ref = head.readline().split(' ')[-1].strip()

    with (git_dir / ref).open('r') as git_hash:
        return git_hash.readline().strip()

我只在我的存储库上测试过此功能,但它似乎工作得相当稳定。

If subprocess isn’t portable and you don’t want to install a package to do something this simple you can also do this.

import pathlib

def get_git_revision(base_path):
    git_dir = pathlib.Path(base_path) / '.git'
    with (git_dir / 'HEAD').open('r') as head:
        ref = head.readline().split(' ')[-1].strip()

    with (git_dir / ref).open('r') as git_hash:
        return git_hash.readline().strip()

I’ve only tested this on my repos but it seems to work pretty consistantly.


回答 5

这是Greg答案的更完整版本:

import subprocess
print(subprocess.check_output(["git", "describe", "--always"]).strip().decode())

或者,如果脚本是从仓库外调用的:

import subprocess, os
os.chdir(os.path.dirname(__file__))
print(subprocess.check_output(["git", "describe", "--always"]).strip().decode())

Here’s a more complete version of Greg’s answer:

import subprocess
print(subprocess.check_output(["git", "describe", "--always"]).strip().decode())

Or, if the script is being called from outside the repo:

import subprocess, os
os.chdir(os.path.dirname(__file__))
print(subprocess.check_output(["git", "describe", "--always"]).strip().decode())

回答 6

如果由于某种原因没有可用的git,但是您有git repo(找到了.git文件夹),则可以从.git / fetch / heads / [branch]获取提交哈希

例如,我使用了在存储库根目录下运行的以下快速且肮脏的Python代码段来获取提交ID:

git_head = '.git\\HEAD'

# Open .git\HEAD file:
with open(git_head, 'r') as git_head_file:
    # Contains e.g. ref: ref/heads/master if on "master"
    git_head_data = str(git_head_file.read())

# Open the correct file in .git\ref\heads\[branch]
git_head_ref = '.git\\%s' % git_head_data.split(' ')[1].replace('/', '\\').strip()

# Get the commit hash ([:7] used to get "--short")
with open(git_head_ref, 'r') as git_head_ref_file:
    commit_id = git_head_ref_file.read().strip()[:7]

If you don’t have git available for some reason, but you have the git repo (.git folder is found), you can fetch the commit hash from .git/fetch/heads/[branch]

For example, I’ve used a following quick-and-dirty Python snippet run at the repository root to get the commit id:

git_head = '.git\\HEAD'

# Open .git\HEAD file:
with open(git_head, 'r') as git_head_file:
    # Contains e.g. ref: ref/heads/master if on "master"
    git_head_data = str(git_head_file.read())

# Open the correct file in .git\ref\heads\[branch]
git_head_ref = '.git\\%s' % git_head_data.split(' ')[1].replace('/', '\\').strip()

# Get the commit hash ([:7] used to get "--short")
with open(git_head_ref, 'r') as git_head_ref_file:
    commit_id = git_head_ref_file.read().strip()[:7]

配置,以便可以从github进行pip安装

问题:配置,以便可以从github进行pip安装

我们想在github上使用pip将私有软件包安装到我们的生产服务器上。这个问题关系到github存储库中需要什么才能使安装成功。

假设使用以下命令行(可以正常身份验证并尝试安装):

pip install git+ssh://git@github.com/BlahCo/search/tree/prod_release_branch/ProductName

ProductName中需要驻留什么?是使用sdist选项运行setup.py之后,tar文件中通常包含的内容,还是实际的tar.gz文件或其他内容?

我在这里问是因为我尝试了几种变体,但无法使其正常工作。任何帮助表示赞赏。

We’d like to use pip with github to install private packages to our production servers. This question concerns what needs to be in the github repo in order for the install to be successful.

Assuming the following command line (which authenticates just fine and tries to install):

pip install git+ssh://git@github.com/BlahCo/search/tree/prod_release_branch/ProductName

What needs to reside in the ProductName? Is it the contents of what would normally be in the tar file after running setup.py with the sdist option, or is the actual tar.gz file, or something else?

I’m asking here because I’ve tried several variations and can’t make it work. Any help appreciated.


回答 0

您需要整个python软件包,其中包含一个setup.py文件。

名为的软件包为foo

foo # the installable package
├── foo
   ├── __init__.py
   └── bar.py
└── setup.py

然后像这样从github安装:

$ pip install git+https://github.com/myuser/foo.git@v123
or
$ pip install git+https://github.com/myuser/foo.git@newbranch

有关更多信息,访问https://pip.pypa.io/en/stable/reference/pip_install/#vcs-support

You need the whole python package, with a setup.py file in it.

A package named foo would be:

foo # the installable package
├── foo
│   ├── __init__.py
│   └── bar.py
└── setup.py

And install from github like:

$ pip install git+ssh://git@github.com/myuser/foo.git
or
$ pip install git+https://github.com/myuser/foo.git@v123
or
$ pip install git+https://github.com/myuser/foo.git@newbranch

More info at https://pip.pypa.io/en/stable/reference/pip_install/#vcs-support


回答 1

我不得不从github repo安装时遇到了类似的问题,但又不想安装git等。

最简单的方法是使用软件包的zip存档。添加/zipball/master到仓库URL:

    $ pip install https://github.com/hmarr/django-debug-toolbar-mongo/zipball/master
Downloading/unpacking https://github.com/hmarr/django-debug-toolbar-mongo/zipball/master
  Downloading master
  Running setup.py egg_info for package from https://github.com/hmarr/django-debug-toolbar-mongo/zipball/master
Installing collected packages: django-debug-toolbar-mongo
  Running setup.py install for django-debug-toolbar-mongo
Successfully installed django-debug-toolbar-mongo
Cleaning up...

这样,您将使ip可以与github源存储库一起使用。

I had similar issue when I had to install from github repo, but did not want to install git , etc.

The simple way to do it is using zip archive of the package. Add /zipball/master to the repo URL:

    $ pip install https://github.com/hmarr/django-debug-toolbar-mongo/zipball/master
Downloading/unpacking https://github.com/hmarr/django-debug-toolbar-mongo/zipball/master
  Downloading master
  Running setup.py egg_info for package from https://github.com/hmarr/django-debug-toolbar-mongo/zipball/master
Installing collected packages: django-debug-toolbar-mongo
  Running setup.py install for django-debug-toolbar-mongo
Successfully installed django-debug-toolbar-mongo
Cleaning up...

This way you will make pip work with github source repositories.


回答 2

如果您要使用requirements.txt文件,则需要使用git类似下面的条目的名称匿名获取您的中的master分支requirements.txt

对于常规安装:

git+git://github.com/celery/django-celery.git

对于“ 可编辑 ”安装:

-e git://github.com/celery/django-celery.git#egg=django-celery

可编辑模式将项目的源代码下载到./src当前目录中。它允许pip freeze输出包的正确github位置。

If you want to use requirements.txt file, you will need git and something like the entry below to anonymously fetch the master branch in your requirements.txt.

For regular install:

git+git://github.com/celery/django-celery.git

For “editable” install:

-e git://github.com/celery/django-celery.git#egg=django-celery

Editable mode downloads the project’s source code into ./src in the current directory. It allows pip freeze to output the correct github location of the package.


回答 3

克隆目标存储库的方式与克隆任何其他项目相同:

git clone git@github.com:myuser/foo.git

然后以开发模式安装它:

cd foo
pip install -e .

您可以更改不需要的任何内容,使用foo包的每个代码都将使用修改后的代码。

该解决方案有两个好处:

  1. 您可以在主项目目录中安装软件包。
  2. 软件包包括.git目录,因此它是常规的Git存储库。您可以立即将叉子推入。

Clone target repository same way like you cloning any other project:

git clone git@github.com:myuser/foo.git

Then install it in develop mode:

cd foo
pip install -e .

You can change anything you wan’t and every code using foo package will use modified code.

There 2 benefits ot this solution:

  1. You can install package in your home projects directory.
  2. Package includes .git dir, so it’s regular Git repository. You can push to your fork right away.

回答 4

您可以在Colab中尝试这种方式

!git clone https://github.com/UKPLab/sentence-transformers.git
!pip install -e /content/sentence-transformers
import sentence_transformers

you can try this way in Colab

!git clone https://github.com/UKPLab/sentence-transformers.git
!pip install -e /content/sentence-transformers
import sentence_transformers

为Python项目添加.gitignore文件的最佳做法?[关闭]

问题:为Python项目添加.gitignore文件的最佳做法?[关闭]

我正在尝试收集一些默认设置,而我意识到我没有标准的一件事是.gitignore文件。有一个很棒的线程显示了适用于Visual Studio项目的.gitignore,但是我没有看到很多关于Python和相关工具(PyGTK,Django)的建议。

到目前为止,我有…

*.pyc
*.pyo

对于已编译的对象

build/
dist/

…用于setuptools输出。

.gitignore文件有哪些最佳实践,我可以在哪里获得更多关于这些最佳实践的信息?

I’m trying to collect some of my default settings, and one thing I realized I don’t have a standard for is .gitignore files. There’s a great thread showing a good .gitignore for Visual Studio projects, but I don’t see many recommendations for Python and related tools (PyGTK, Django).

So far, I have…

*.pyc
*.pyo

…for the compiled objects and…

build/
dist/

…for the setuptools output.

What are some best practices for .gitignore files, and where can I go for more about these best practices?


回答 0

当使用buildout时,我在.gitignore(以及*.pyo和中*.pyc)有以下内容:

.installed.cfg
bin
develop-eggs
dist
downloads
eggs
parts
src/*.egg-info
lib
lib64

感谢Jacob Kaplan-Moss

我也倾向于加入.svn,因为我们在工作的地方使用了多个SCM。

When using buildout I have following in .gitignore (along with *.pyo and *.pyc):

.installed.cfg
bin
develop-eggs
dist
downloads
eggs
parts
src/*.egg-info
lib
lib64

Thanks to Jacob Kaplan-Moss

Also I tend to put .svn in since we use several SCM-s where I work.


回答 1

Github有一个很棒的样板.gitignore

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]

# C extensions
*.so

# Distribution / packaging
bin/
build/
develop-eggs/
dist/
eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
.tox/
.coverage
.cache
nosetests.xml
coverage.xml

# Translations
*.mo

# Mr Developer
.mr.developer.cfg
.project
.pydevproject

# Rope
.ropeproject

# Django stuff:
*.log
*.pot

# Sphinx documentation
docs/_build/

Github has a great boilerplate .gitignore

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]

# C extensions
*.so

# Distribution / packaging
bin/
build/
develop-eggs/
dist/
eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
.tox/
.coverage
.cache
nosetests.xml
coverage.xml

# Translations
*.mo

# Mr Developer
.mr.developer.cfg
.project
.pydevproject

# Rope
.ropeproject

# Django stuff:
*.log
*.pot

# Sphinx documentation
docs/_build/

回答 2

local_settings.py,用于Django项目。

*〜适用于所有项目。

local_settings.py, for django projects.

*~ for all projects.


回答 3

涵盖了大多数常规内容-

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# pyenv
.python-version

# celery beat schedule file
celerybeat-schedule

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/

参考:python .gitignore

Covers most of the general stuff –

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# pyenv
.python-version

# celery beat schedule file
celerybeat-schedule

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/

Reference: python .gitignore


回答 4

一个问题是您是否还想使用git来部署项目。如果是这样,则您可能希望从存储库中排除本地sqlite文件,这同样适用于文件上传(主要是在您的媒体文件夹中)。(我现在正在谈论django,因为您的问题也被标记为django)

One question is if you also want to use git for the deploment of your projects. If so you probably would like to exclude your local sqlite file from the repository, same probably applies to file uploads (mostly in your media folder). (I’m talking about django now, since your question is also tagged with django)


回答 5

这是setuptools可能遗留下来的一些其他文件:

MANIFEST
*.egg-info

Here are some other files that may be left behind by setuptools:

MANIFEST
*.egg-info