How to update a protected branch in python-gitlab? - python-gitlab

I am using python-gitlab to help configure projects. I'm trying to automate going into GitLab Settings > Repository > Protected branches, then for the existing master branch, changing "Allowed to merge" from "Maintainers" to "Developers + Maintainers". Here's a code snippet:
import gitlab
gl = gitlab.Gitlab.from_config()
project = project = gl.projects.get("my-team/my_project")
master_branch = project.protectedbranches.get("master")
print(master_branch.merge_access_levels)
The data type is just is a list of dicts; there doesn't appear to be a way to update the setting like other settings in this API. Even is you just update it:
master_branch.merge_access_levels[0]['access_level'] = 30
project.save()
nothing happens. Is there a way to do this with python-gitlab?

For what I've done in my project, I didn't find any way to update. So I just remove the wanted branch from protected branch list and then create the protected branch again by referring to the official docs.
project.protectedbranches.delete('master')
project.protectedbranches.create({
'name': 'master',
'merge_access_level': gitlab.const.AccessLevel.NO_ACCESS,
'push_access_level': gitlab.const.AccessLevel.NO_ACCESS,
'allow_force_push': False
})
And this works really well.
Attention: the delete/create actions just remove/put the wanted branch from/into protected branches list, there is no risk with the branch itself.

You are looking for branch.protect():
branch = project.branches.get('master')
branch.protect(developers_can_push=True, developers_can_merge=True)

Related

I need a faster way to find repos with PyGitHub

My organization has a ton of repos in GitHub. I have a subset of these repos that I want to programmatically detect. All of the repos I want start with the same string.
The code below correctly finds them, but it takes about a minute to do it because of all of the repos present. Is there a faster way to do this?
def FindMyRepos():
global ACCESS_TOKEN, ORGANIZATION, PREFIX
repos = []
# Log into GitHub
gh = Github(ACCESS_TOKEN)
if not gh: return 1
# Connect to organization
org = gh.get_organization(ORGANIZATION)
if not org: return 2
# Look for repos that start with PREFIX
for r in org.get_repos():
if r.name.startswith(PREFIX):
repos.append(r.name)
After a lot of searching, I have found the answer. I need to use the search_repositories method. The one 'gotcha' is that this method is part of the Github object but not the organization object.
def FindMyRepos():
global ACCESS_TOKEN, ORGANIZATION, PREFIX
repos = []
# Log into GitHub
gh = Github(ACCESS_TOKEN)
if not gh: return 1
# Look for repos in ORGANIZATION containing PREFIX
for r in gh.search_repositorires(query=ORGANIZATION+'/'+PREFIX):
# Only include repos starting with PREFIX
if r.name.startswith(PREFIX):
repos.append(r.name)
The reason for the if r.name.startswith(PREFIX) is because for some reason the search will return all repos in the ORGANIZATION that have the string PREFIX in their name anywhere.

Different const value for dev and prod env in go

For example I'm developing guestbook. It allows to add records, that need to be approved by moderator. But when developing further functionality, it's a pain to approve each record, that is added during testing.
So, is it possible to build dev version of application that creates such records with appropriate flag set?
For example prod build is compiled with the following fucntion:
func NewRecord() Record {
return Record{Moderation: Awaiting}
}
And in dev build is compiled with:
func NewRecord() Record {
return Record{Moderation: Approved}
}
I know in frontend, when you building some JS app, it's a common practice to set NODE_ENV=production environment variable when building for production.
I'm looking for something similar in Go.
I see two ways, but don't like any of them:
I can just set Awaiting = Approved while developing and then change it back to actual value when building prod version. But I afraid that one day I will forget about this mock, will commit it to repo or something like that.
Change function to something like
func NewRecord() Record {
if os.Getenv(mykey) == "production" {
return Record{Moderation: Awaiting}
} else {
return Record{Moderation: Approved}
}
}
But I don't like that this condition is evaluated in runtime for each new record. It just seems to be wrong for compiled language.
As a bonus it would be nice, if such application can show warning (to stdout/stderr) if it's build as dev version.
Thanks.
Personally I think using environment variables as you've done in your example is the "correct" way to do this. This allows you to tweak the behavior without rebuilding the application, which may be very useful while debugging. However, if you really want this done at compile time it can be accomplished with build constraints.
A built constraint is a special comment placed at the top of a file that says "only build this file if a specific condition is met". Conditions can be things like the machine architecture we're building on, the OS we're running, or custom build tags that the user specifies when building.
For example, you could tweak your function to something like:
func NewRecord() Record {
return Record{Moderation: ModLevel}
}
and then define ModLevel twice, once in a file modlevel_prod.go that looks like this (Note that the syntax for build constraints is changing, the second line is the new syntax and the first one wont' be valid for much longer. See the link at the end of this post for more info):
// +build !dev
//go:build !dev
package mypackage
const ModLevel = Awaiting
and one in modlevel_dev.go (or whever, the filename doesn't matter in this case) that looks like this:
// +build dev
//go:build dev
package mypackage
const ModLevel = Approved
Now, when you build, the production version of the file is the default and if you want to build the dev version of the file you have to explicitly include the build tag:
$ go build -tags dev
EDIT: the syntax for build constraints is changing. I have updated the examples to include both the old and new versions (which currently coexist). See https://golang.org/issues/41184 for more info and a timeline for deprecating the old one.
Evaluate the expression once at package initialization:
var defaultModeration int
func init() {
if os.Getenv(mykey) == "production" {
defaultModeration = Awaiting
} else {
defaultModeration = Approved
}
}
func NewRecord() Record { return Record{Moderation: defaultModeration} }

Can a build template be based on another build template on TeamCity?

I am using TeamCity 9.0.2, and I would like to make a template implement another template, or make a build configuration implement more than one template.
Can this be achieved?
This was not available when you asked the question but since Team City 10 you can now use Kotlin to configure your builds and thus your templates.
From this you can make Templates implement other Templates.
I myself have made Templates inherit from other templates to cut down on reconfiguration time and to not have to repeat myself so many times.
open class TheBaseTemplate(uuidIn: String, extIdIn: String, nameIn: String, additionalSettings: Template.() -> Unit) : Template({
uuid = uuidIn
extId = extIdIn
name = nameIn
/* all the other settings that are the same for the derived templates*/
additionalSettings()
})
object DerivedTemplateA : TheBaseTemplate("myUuidA", "myExtIdA", "myNameA", {
params {
param("set this", "to this")
}
})
object DerivedTemplateB : TheBaseTemplate("myUuidB", "myExtIdB", "myNameB", {
params {
param("set this", "to that")
}
})
object Project : Project({
uuid = "project uuid"
extId = "project extid"
name = "project name"
buildType {
template(DerivedTemplateA)
/* the uuid, extId and name are set here */
}
buildType {
template(DerivedTemplateB)
/* the uuid, extId and name are set here */
}
template(DerivedTemplateA)
template(DerivedTemplateB)
})
The above code might be very hard to understand. It will take some time to familiarise yourself with Kotlin, what it does, and how it interfaces with TeamCity. I should point out that some imports are missing.
Additionally, take the example with a pinch of salt. It is a quick example to demonstrate one way of templates implementing other templates. Do not take this example as the definitive way to do things.
Unfortunately, this is currently not possible but already requested for a long time in TW-12153 (maybe you would like to vote for it).
To share several build steps among several build configurations or build configuration templates, I am using meta runners:
A Meta-Runner allows you to extract build steps, requirements and parameters from a build configuration and create a build runner out of them.
This build runner can then be used as any other build runner in a build step of any other build configuration or template.
Although using meta runners works as a workaround for us, editing meta runners is not as convenient as editing a build configuration template (as it usually requires editing the meta runner definition XML file by hand).
Update 2021
As #zosal points out in his answer TeamCity meanwhile provides another way of sharing common build configuration data or logic by means of the Kotlin DSL. The Kotlin DSL is a very powerful tool but may not always fit in your specific scenario. I would recommend to at least give it a try or watch one of the introductory tutorial videos.

Ruby-Git gets wrong tag info

using ruby-git, a private repo cloned from github located at PATH_TO_REPO_NAME
g = Git.open("PATH_TO_REPO_NAME")
tag="us_prod"
puts g.tag(tag)
result is not what appears under https://github.com/gtforge/REPO_NAME/tags
error eventually was that while I pulled from the repo, I didnt fetch the tags separately, which is because I did not know that was required. false alarm.

Setting the branch when comitting via the github API

I am using the a ruby interface to the github API to push content from a ruby app to github. All works well, but I can't seem to find how to specify the branch when using the Github API.
github = Github.new :oauth_token => 'MYPERSONALACCESSTOKEN'
blobs = github.git_data.blobs.create 'whoisstan', 'test_for_github_api', content: 'Sample', encoding: 'utf-8'
trees = github.git_data.trees.create 'whoisstan', 'test_for_github_api',
"tree" => [
{
"path" => "file.rb",
"mode" => "100644",
"type" => "blob",
"sha" => blobs.body['sha']
}
]
commits = github.git_data.commits.create 'whoisstan', 'test_for_github_api', :tree => trees.body['sha'], :message => 'new version', :parents => nil
Also strangely the content doesn't appear linked in the website, I can only get to it when I use the html_url of the response.
https://github.com/whoisstan/test_for_github_api/commit/f349a436e2fdbffddfc6715490da7282160798fe
What looks suspicious is that the commit parent appears to be null. Also the post commit hooks are not triggered. How do I specify the branch and let the content appear proper in the github website?
The documentation over at https://developer.github.com/v3/git/ explains the process for adding a commit to a specific branch. The things you need to do differently are:
set the parent of the commit to point to the previous commit on top of which you are creating this commit
modify the reference for the branch you want to update so that it points to the created commit
The Git Data API is a low-level API which works with blobs, trees, commits and refs. In some cases, you need this flexibility to achieve the goals of your application. In other cases, you might consider using a higher-level API, like the Repo Contents API, which works with files (and creates all the necessary Git objects behind the scenes)

Resources