Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds open source licenses (fixes #2941) #3982

Open
wants to merge 31 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
bc45df6
added test case to check licenses for unmodified placeholders, used d…
alexzvk Nov 26, 2022
7288f07
adjusted pathing for importing title dictionary
alexzvk Nov 27, 2022
c91a5d2
reworked pathing for titles_dict with os.chdir
alexzvk Nov 27, 2022
9a39937
stored licenses to filename mapping in json file
alexzvk Nov 28, 2022
366a722
adjusted json, dict errors
alexzvk Nov 28, 2022
433fb01
Merge pull request #1 from alexzvk/2941
alexzvk Nov 28, 2022
61ddb4d
changed placeholder unit test to a script
alexzvk Nov 28, 2022
3045e4f
added temporary placeholder file
alexzvk Nov 29, 2022
2923cc8
fiddling with licenses.json file pathing
alexzvk Nov 29, 2022
de9a6b6
fiddling with licenses.json file pathing
alexzvk Nov 29, 2022
c4ec9cd
fiddling with licenses.json file pathing
alexzvk Nov 29, 2022
6f7b3e3
fiddling with licenses.json file pathing
alexzvk Nov 29, 2022
5c37807
fiddling with licenses.json file pathing
alexzvk Nov 29, 2022
cda8b5b
fiddling with licenses.json file pathing
alexzvk Nov 29, 2022
699272c
fiddling with licenses.json file pathing
alexzvk Nov 29, 2022
15109ab
still fiddling with licenses file pathing
alexzvk Nov 29, 2022
b0699ce
moved licenses.json to avoid filepathing issues hopefully?
alexzvk Nov 29, 2022
b869dc4
fixes bug for when a not open source license is selected
alexzvk Nov 29, 2022
5f9d74a
forgot newline character to fix previous bug
alexzvk Nov 29, 2022
ebbe28d
style changes
alexzvk Nov 29, 2022
1775bf0
trying to fix confusing colon error
alexzvk Nov 29, 2022
b0e361d
now passing all pre-commit checks
alexzvk Nov 29, 2022
73a7ed4
fixed dictionary error where key queries had newlines
alexzvk Nov 29, 2022
a05722f
forgot variable assignment for previous commit
alexzvk Nov 29, 2022
96e5f85
fixed bug with not open source option
alexzvk Dec 5, 2022
d03cc15
attempt at workaround using temporary placeholder
alexzvk Dec 5, 2022
b6e5d49
fixed temporary workaround quotes error
alexzvk Dec 5, 2022
7fa6b94
Merge pull request #2 from alexzvk/2941
alexzvk Dec 5, 2022
d6dea41
Merge branch 'refs/heads/master' into all-oss-licenses
browniebroke May 13, 2024
9106979
Remove default value for GITHUB_TOKEN to avoid triggering secret scan…
browniebroke May 13, 2024
0526440
Reformat files using pre-commit
browniebroke May 13, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .github/workflows/update-licenses.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Update Licenses

on:
# Every day at 2am
schedule:
- cron: "0 2 * * *"
# Manual trigger
workflow_dispatch:

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2

- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.8
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Update list
run: python scripts/update_licenses.py
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Commit changes
uses: stefanzweifel/[email protected]
with:
commit_message: Update Licenses
file_pattern: cookiecutter.json {{cookiecutter.project_slug}}/licenses/*.txt
51 changes: 46 additions & 5 deletions cookiecutter.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,52 @@
"email": "{{ cookiecutter.author_name.lower() | trim() |replace(' ', '-') }}@{{ cookiecutter.domain_name.lower() | trim() }}",
"version": "0.1.0",
"open_source_license": [
"MIT",
"BSD",
"GPLv3",
"Apache Software License 2.0",
"Not open source"
"Not open source",
"MIT License",
"BSD 3-Clause \"New\" or \"Revised\" License",
"GNU General Public License v3.0",
"Apache License 2.0",
"\"Do What The F*ck You Want To Public License\"",
"Academic Free License v3.0",
"Artistic License 2.0",
"BSD 2-Clause \"Simplified\" License",
"BSD 3-Clause Clear License",
"BSD 4-Clause \"Original\" or \"Old\" License",
"BSD Zero Clause License",
"Boost Software License 1.0",
"CERN Open Hardware Licence Version 2 - Permissive",
"CERN Open Hardware Licence Version 2 - Strongly Reciprocal",
"CERN Open Hardware Licence Version 2 - Weakly Reciprocal",
"CeCILL Free Software License Agreement v2.1",
"Creative Commons Attribution 4.0 International",
"Creative Commons Attribution Share Alike 4.0 International",
"Creative Commons Zero v1.0 Universal",
"Eclipse Public License 1.0",
"Eclipse Public License 2.0",
"Educational Community License v2.0",
"European Union Public License 1.1",
"European Union Public License 1.2",
"GNU Affero General Public License v3.0",
"GNU Free Documentation License v1.3",
"GNU General Public License v2.0",
"GNU Lesser General Public License v2.1",
"GNU Lesser General Public License v3.0",
"ISC License",
"LaTeX Project Public License v1.3c",
"MIT No Attribution",
"Microsoft Public License",
"Microsoft Reciprocal License",
"Mozilla Public License 2.0",
"Mulan Permissive Software License, Version 2",
"Open Data Commons Open Database License v1.0",
"Open Software License 3.0",
"PostgreSQL License",
"SIL Open Font License 1.1",
"The Unlicense",
"Universal Permissive License v1.0",
"University of Illinois/NCSA Open Source License",
"Vim License",
"zlib License"
],
"username_type": ["username", "email"],
"timezone": "UTC",
Expand Down
47 changes: 31 additions & 16 deletions hooks/post_gen_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,6 @@
DEBUG_VALUE = "debug"


def remove_open_source_files():
file_names = ["CONTRIBUTORS.txt", "LICENSE"]
for file_name in file_names:
os.remove(file_name)


def remove_gplv3_files():
file_names = ["COPYING"]
for file_name in file_names:
os.remove(file_name)


def remove_custom_user_manager_files():
os.remove(
os.path.join(
Expand Down Expand Up @@ -434,6 +422,36 @@ def remove_drf_starter_files():
os.remove(os.path.join("{{cookiecutter.project_slug}}", "users", "tests", "test_swagger.py"))


def handle_licenses():
special_license_files = {
"European Union Public License 1.1": "COPYING",
"GNU General Public License v3.0": "COPYING",
"GNU Lesser General Public License v3.0": "COPYING.LESSER",
"The Unlicense": "UNLICENSE",
}

selected_title = """{{ cookiecutter.open_source_license }}"""

if selected_title == "Not open source":
os.remove("CONTRIBUTORS.txt")
shutil.rmtree("licenses")
return

with open(os.path.join("licenses", "licenses.json")) as f:
titles_dict = json.load(f)
# access the title to filename dictionary to find the correct file
# using a dictionary instead of looping reduces time complexity
with open(os.path.join("licenses", titles_dict[selected_title])) as f:
contents = f.readlines()

with open(special_license_files.get(titles_dict[selected_title], "LICENSE"), "w") as f:
# +2 to get rid of the --- and and an extra new line
i = contents.index("---\n", 1) + 2
f.writelines(contents[i:])

shutil.rmtree("licenses")


def main():
debug = "{{ cookiecutter.debug }}".lower() == "y"

Expand All @@ -444,10 +462,7 @@ def main():
)
set_flags_in_settings_files()

if "{{ cookiecutter.open_source_license }}" == "Not open source":
remove_open_source_files()
if "{{ cookiecutter.open_source_license}}" != "GPLv3":
remove_gplv3_files()
handle_licenses()

if "{{ cookiecutter.username_type }}" == "username":
remove_custom_user_manager_files()
Expand Down
39 changes: 39 additions & 0 deletions scripts/check_licenses.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import os
import re
from pprint import pprint


# script to check if licenses generated have placeholders not replaced
def check_scripts_for_placeholders():
brackets = []
for filename in os.listdir("../{{cookiecutter.project_slug}}/licenses"):
file = open("../{{cookiecutter.project_slug}}/licenses/" + filename, encoding="utf8")

# 'found' stores all found bracket instances
found = []

# dashes counts the '---\n' lines in the licenses.
# it skips instances of brackets until after 2 as to skip the jekyll header
dashes = 0
for i, line in enumerate(file.readlines()):
if line == "---\n":
dashes += 1
# skips any possible brackets until the jekyll header is skipped
if dashes < 2:
continue
line = re.findall(r"\[.*\]", line)
if line != []:
found += (i, line)

# add any found instances of placeholders to the brackets array
# print it after the loop is executed
if found != []:
brackets += (filename, found)
if len(brackets) > 0:
print()
pprint(brackets)
assert len(brackets) == 0


if __name__ == "__main__":
check_scripts_for_placeholders()
69 changes: 69 additions & 0 deletions scripts/update_licenses.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import codecs
import json
import os
import re
from pathlib import Path

from github import Github

CURRENT_FILE = Path(__file__)
ROOT = CURRENT_FILE.parents[1]
GITHUB_TOKEN = os.getenv("GITHUB_TOKEN")


def main() -> None:
"""
Script entry point.
"""
titles_dict = {}
repo = Github(login_or_token=GITHUB_TOKEN).get_repo("github/choosealicense.com")
license_dir = ROOT / "{{cookiecutter.project_slug}}" / "licenses"
license_dir.mkdir(exist_ok=True)
for file in repo.get_contents("_licenses", "gh-pages"):
content = codecs.decode(file.decoded_content)
# make below line into a dictionary mapping to filename
titles_dict[content.split("\n", maxsplit=2)[1].replace("title: ", "")] = file.name
path = license_dir / file.name
if not path.is_file():
path.touch()
(license_dir / file.name).write_text(replace_content_options(content))

# write the titles dictionary to a json file so it can be accessed by other files
with open("{{cookiecutter.project_slug}}/licenses/licenses.json", "w") as licenses_dict:
json.dump(titles_dict, licenses_dict, indent=2)
# Put "Not open source" at front so people know it's an option
front_options = [
"Not open source",
"MIT License",
'BSD 3-Clause "New" or "Revised" License',
"GNU General Public License v3.0",
"Apache License 2.0",
]
# update to iterate through dictionary
titles = [x for x in sorted(titles_dict.keys()) if x not in front_options]
update_cookiecutter(front_options + titles)


year = (re.compile(r"\[year]"), "{% now 'utc', '%Y' %}")
email = (re.compile(r"\[email]"), "{{ cookiecutter.email }}")
fullname = (re.compile(r"\[fullname]"), "{{ cookiecutter.author_name }}")
project = (re.compile(r"\[project]"), "{{ cookiecutter.project_name }}")
projecturl = (re.compile(r"\[projecturl]"), "{{ cookiecutter.domain_name }}")


def replace_content_options(content) -> str:
for compiled, replace in (year, email, fullname, project, projecturl):
content = compiled.sub(replace, content)
return content


def update_cookiecutter(titles: list):
with open("cookiecutter.json") as f:
data = json.load(f)
data["open_source_license"] = titles
with open("cookiecutter.json", "w") as f:
json.dump(data, f, indent=2)


if __name__ == "__main__":
main()
17 changes: 12 additions & 5 deletions tests/test_cookiecutter_generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,21 @@ def context():
}


def generate_license_file_titles():
directory = os.path.join("{{cookiecutter.project_slug}}", "licenses")
titles = []
for file in os.listdir(directory):
if file == "-temporary-placeholder.txt":
continue
with open(os.path.join(directory, file)) as f:
titles.append(f.readlines()[1].replace("title: ", "").replace("\n", ""))
return titles


SUPPORTED_COMBINATIONS = [
*[{"open_source_license": x} for x in generate_license_file_titles()],
{"username_type": "username"},
{"username_type": "email"},
{"open_source_license": "MIT"},
{"open_source_license": "BSD"},
{"open_source_license": "GPLv3"},
{"open_source_license": "Apache Software License 2.0"},
{"open_source_license": "Not open source"},
{"windows": "y"},
{"windows": "n"},
{"editor": "None"},
Expand Down
7 changes: 7 additions & 0 deletions {{cookiecutter.project_slug}}/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,10 @@ You can find a list of available variables [in the bootstrap source](https://git

Bootstrap's javascript as well as its dependencies are concatenated into a single file: `static/js/vendors.js`.
{%- endif %}

{% if cookiecutter.open_source_license != "Not open source" %}
License
^^^^^^

Licensed under the {{cookiecutter.open_source_license}}
{% endif %}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{ cookiecutter.open_source_license }}
41 changes: 41 additions & 0 deletions {{cookiecutter.project_slug}}/licenses/0bsd.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
title: BSD Zero Clause License
spdx-id: 0BSD

description: The BSD Zero Clause license goes further than the BSD 2-Clause license to allow you unlimited freedom with the software without requirements to include the copyright notice, license text, or disclaimer in either source or binary forms.

how: Create a text file (typically named LICENSE or LICENSE.txt) in the root of your source code and copy the text of the license into the file. Replace {% now 'utc', '%Y' %} with the current year and {{ cookiecutter.author_name }} with the name (or names) of the copyright holders. You may take the additional step of removing the copyright notice.

using:
gatsby-starter-default: https://github.com/gatsbyjs/gatsby-starter-default/blob/master/LICENSE
Toybox: https://github.com/landley/toybox/blob/master/LICENSE
PickMeUp: https://github.com/nazar-pc/PickMeUp/blob/master/copying.md

permissions:
- commercial-use
- distribution
- modifications
- private-use

conditions: []

limitations:
- liability
- warranty

---

BSD Zero Clause License

Copyright (c) {% now 'utc', '%Y' %} {{ cookiecutter.author_name }}

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
Loading
Loading