Merge branch 'develop' into gc-perms
This commit is contained in:
commit
d13ac4b17c
21 changed files with 150 additions and 92 deletions
|
|
@ -98,15 +98,17 @@ context("Kanban Board", () => {
|
|||
});
|
||||
|
||||
it("Checks if Kanban Board edits are blocked for non-System Manager and non-owner of the Board", () => {
|
||||
// create admin kanban board
|
||||
cy.call("frappe.tests.ui_test_helpers.create_todo", { description: "Frappe User ToDo" });
|
||||
|
||||
cy.switch_to_user("Administrator");
|
||||
cy.call("frappe.tests.ui_test_helpers.create_admin_kanban");
|
||||
// remove sys manager
|
||||
cy.remove_role("frappe@example.com", "System Manager");
|
||||
|
||||
cy.switch_to_user("frappe@example.com");
|
||||
const noSystemManager = "nosysmanager@example.com";
|
||||
cy.call("frappe.tests.ui_test_helpers.create_test_user", {
|
||||
username: noSystemManager,
|
||||
});
|
||||
cy.remove_role(noSystemManager, "System Manager");
|
||||
cy.call("frappe.tests.ui_test_helpers.create_todo", { description: "Frappe User ToDo" });
|
||||
cy.call("frappe.tests.ui_test_helpers.create_admin_kanban");
|
||||
|
||||
cy.switch_to_user(noSystemManager);
|
||||
|
||||
cy.visit("/app/todo/view/kanban/Admin Kanban");
|
||||
|
||||
|
|
@ -122,7 +124,8 @@ context("Kanban Board", () => {
|
|||
// Column actions should be hidden (dropdown for 'Archive' and indicators)
|
||||
cy.get(".kanban .column-options").should("have.length", 0);
|
||||
|
||||
cy.add_role("frappe@example.com", "System Manager");
|
||||
cy.switch_to_user("Administrator");
|
||||
cy.call("frappe.client.delete", { doctype: "User", name: noSystemManager });
|
||||
});
|
||||
|
||||
after(() => {
|
||||
|
|
|
|||
|
|
@ -1274,7 +1274,7 @@ def reload_doc(
|
|||
return frappe.modules.reload_doc(module, dt, dn, force=force, reset_permissions=reset_permissions)
|
||||
|
||||
|
||||
@whitelist()
|
||||
@whitelist(methods=["POST", "PUT"])
|
||||
def rename_doc(
|
||||
doctype: str,
|
||||
old: str,
|
||||
|
|
|
|||
|
|
@ -254,19 +254,23 @@ def address_query(doctype, txt, searchfield, start, page_len, filters):
|
|||
"""select
|
||||
`tabAddress`.name, `tabAddress`.city, `tabAddress`.country
|
||||
from
|
||||
`tabAddress`, `tabDynamic Link`
|
||||
`tabAddress`
|
||||
join `tabDynamic Link`
|
||||
on (`tabDynamic Link`.parent = `tabAddress`.name and `tabDynamic Link`.parenttype = 'Address')
|
||||
where
|
||||
`tabDynamic Link`.parent = `tabAddress`.name and
|
||||
`tabDynamic Link`.parenttype = 'Address' and
|
||||
`tabDynamic Link`.link_doctype = %(link_doctype)s and
|
||||
`tabDynamic Link`.link_name = %(link_name)s and
|
||||
ifnull(`tabAddress`.disabled, 0) = 0 and
|
||||
({search_condition})
|
||||
{mcond} {condition}
|
||||
order by
|
||||
if(locate(%(_txt)s, `tabAddress`.name), locate(%(_txt)s, `tabAddress`.name), 99999),
|
||||
case
|
||||
when locate(%(_txt)s, `tabAddress`.name) != 0
|
||||
then locate(%(_txt)s, `tabAddress`.name)
|
||||
else 99999
|
||||
end,
|
||||
`tabAddress`.idx desc, `tabAddress`.name
|
||||
limit %(start)s, %(page_len)s """.format(
|
||||
limit %(page_len)s offset %(start)s""".format(
|
||||
mcond=get_match_cond(doctype),
|
||||
search_condition=search_condition,
|
||||
condition=condition or "",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
# Copyright (c) 2015, Frappe Technologies and Contributors
|
||||
# License: MIT. See LICENSE
|
||||
from functools import partial
|
||||
|
||||
import frappe
|
||||
from frappe.contacts.doctype.address.address import get_address_display
|
||||
from frappe.contacts.doctype.address.address import address_query, get_address_display
|
||||
from frappe.tests.utils import FrappeTestCase
|
||||
|
||||
|
||||
|
|
@ -28,3 +30,29 @@ class TestAddress(FrappeTestCase):
|
|||
address = frappe.get_list("Address")[0].name
|
||||
display = get_address_display(frappe.get_doc("Address", address).as_dict())
|
||||
self.assertTrue(display)
|
||||
|
||||
def test_address_query(self):
|
||||
def query(doctype="Address", txt="", searchfield="name", start=0, page_len=20, filters=None):
|
||||
if filters is None:
|
||||
filters = {"link_doctype": "User", "link_name": "Administrator"}
|
||||
return address_query(doctype, txt, searchfield, start, page_len, filters)
|
||||
|
||||
frappe.get_doc(
|
||||
{
|
||||
"address_type": "Billing",
|
||||
"address_line1": "1",
|
||||
"city": "Mumbai",
|
||||
"state": "Maharashtra",
|
||||
"country": "India",
|
||||
"doctype": "Address",
|
||||
"links": [
|
||||
{
|
||||
"link_doctype": "User",
|
||||
"link_name": "Administrator",
|
||||
}
|
||||
],
|
||||
}
|
||||
).insert()
|
||||
|
||||
self.assertGreaterEqual(len(query(txt="Admin")), 1)
|
||||
self.assertEqual(len(query(txt="what_zyx")), 0)
|
||||
|
|
|
|||
|
|
@ -165,7 +165,7 @@ def _make(
|
|||
if not comm.get_outgoing_email_account():
|
||||
frappe.throw(
|
||||
_(
|
||||
"Unable to send mail because of a missing email account. Please setup default Email Account from Setup > Email > Email Account"
|
||||
"Unable to send mail because of a missing email account. Please setup default Email Account from Settings > Email Account"
|
||||
),
|
||||
exc=frappe.OutgoingEmailError,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ class CommunicationEmailMixin:
|
|||
if include_sender:
|
||||
cc.append(self.sender_mailid)
|
||||
if is_inbound_mail_communcation:
|
||||
if (doc_owner := self.get_owner()) not in frappe.STANDARD_USERS:
|
||||
if (doc_owner := self.get_owner()) and (doc_owner not in frappe.STANDARD_USERS):
|
||||
cc.append(doc_owner)
|
||||
cc = set(cc) - {self.sender_mailid}
|
||||
cc.update(self.get_assignees())
|
||||
|
|
@ -216,7 +216,11 @@ class CommunicationEmailMixin:
|
|||
"reference_name": self.reference_name,
|
||||
"reference_type": self.reference_doctype,
|
||||
}
|
||||
return ToDo.get_owners(filters)
|
||||
|
||||
if self.reference_doctype and self.reference_name:
|
||||
return ToDo.get_owners(filters)
|
||||
else:
|
||||
return []
|
||||
|
||||
@staticmethod
|
||||
def filter_thread_notification_disbled_users(emails):
|
||||
|
|
|
|||
|
|
@ -169,7 +169,6 @@ class ServerScript(Document):
|
|||
return items
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def setup_scheduler_events(script_name, frequency):
|
||||
"""Creates or Updates Scheduled Job Type documents based on the specified script name and frequency
|
||||
|
||||
|
|
|
|||
|
|
@ -219,7 +219,10 @@ frappe.ui.form.on("User", {
|
|||
});
|
||||
}
|
||||
|
||||
if (frappe.session.user == doc.name || frappe.user.has_role("System Manager")) {
|
||||
if (
|
||||
cint(frappe.boot.sysdefaults.enable_two_factor_auth) &&
|
||||
(frappe.session.user == doc.name || frappe.user.has_role("System Manager"))
|
||||
) {
|
||||
frm.add_custom_button(
|
||||
__("Reset OTP Secret"),
|
||||
function () {
|
||||
|
|
|
|||
|
|
@ -16,38 +16,27 @@ frappe.ui.form.on("Bulk Update", {
|
|||
if (!frm.doc.update_value) {
|
||||
frappe.throw(__('Field "value" is mandatory. Please specify value to be updated'));
|
||||
} else {
|
||||
frappe
|
||||
.call({
|
||||
method: "frappe.desk.doctype.bulk_update.bulk_update.update",
|
||||
args: {
|
||||
doctype: frm.doc.document_type,
|
||||
field: frm.doc.field,
|
||||
value: frm.doc.update_value,
|
||||
condition: frm.doc.condition,
|
||||
limit: frm.doc.limit,
|
||||
},
|
||||
})
|
||||
.then((r) => {
|
||||
let failed = r.message;
|
||||
if (!failed) failed = [];
|
||||
frm.call("bulk_update").then((r) => {
|
||||
let failed = r.message;
|
||||
if (!failed) failed = [];
|
||||
|
||||
if (failed.length && !r._server_messages) {
|
||||
frappe.throw(
|
||||
__("Cannot update {0}", [
|
||||
failed.map((f) => (f.bold ? f.bold() : f)).join(", "),
|
||||
])
|
||||
);
|
||||
} else {
|
||||
frappe.msgprint({
|
||||
title: __("Success"),
|
||||
message: __("Updated Successfully"),
|
||||
indicator: "green",
|
||||
});
|
||||
}
|
||||
if (failed.length && !r._server_messages) {
|
||||
frappe.throw(
|
||||
__("Cannot update {0}", [
|
||||
failed.map((f) => (f.bold ? f.bold() : f)).join(", "),
|
||||
])
|
||||
);
|
||||
} else {
|
||||
frappe.msgprint({
|
||||
title: __("Success"),
|
||||
message: __("Updated Successfully"),
|
||||
indicator: "green",
|
||||
});
|
||||
}
|
||||
|
||||
frappe.hide_progress();
|
||||
frm.save();
|
||||
});
|
||||
frappe.hide_progress();
|
||||
frm.save();
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -10,26 +10,24 @@ from frappe.utils.scheduler import is_scheduler_inactive
|
|||
|
||||
|
||||
class BulkUpdate(Document):
|
||||
pass
|
||||
@frappe.whitelist()
|
||||
def bulk_update(self):
|
||||
self.check_permission("write")
|
||||
limit = self.limit if self.limit and cint(self.limit) < 500 else 500
|
||||
|
||||
condition = ""
|
||||
if self.condition:
|
||||
if ";" in self.condition:
|
||||
frappe.throw(_("; not allowed in condition"))
|
||||
|
||||
@frappe.whitelist()
|
||||
def update(doctype, field, value, condition="", limit=500):
|
||||
if not limit or cint(limit) > 500:
|
||||
limit = 500
|
||||
condition = f" where {self.condition}"
|
||||
|
||||
if condition:
|
||||
condition = " where " + condition
|
||||
|
||||
if ";" in condition:
|
||||
frappe.throw(_("; not allowed in condition"))
|
||||
|
||||
docnames = frappe.db.sql_list(
|
||||
f"""select name from `tab{doctype}`{condition} limit {limit} offset 0"""
|
||||
)
|
||||
data = {}
|
||||
data[field] = value
|
||||
return submit_cancel_or_update_docs(doctype, docnames, "update", data)
|
||||
docnames = frappe.db.sql_list(
|
||||
f"""select name from `tab{self.document_type}`{condition} limit {limit} offset 0"""
|
||||
)
|
||||
return submit_cancel_or_update_docs(
|
||||
self.document_type, docnames, "update", {self.field: self.update_value}
|
||||
)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
|
|
|
|||
|
|
@ -325,7 +325,7 @@ class EmailAccount(Document):
|
|||
|
||||
if _raise_error:
|
||||
frappe.throw(
|
||||
_("Please setup default Email Account from Setup > Email > Email Account"),
|
||||
_("Please setup default Email Account from Settings > Email Account"),
|
||||
frappe.OutgoingEmailError,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -69,9 +69,7 @@ class SMTPServer:
|
|||
|
||||
if not self.server:
|
||||
frappe.msgprint(
|
||||
_(
|
||||
"Email Account not setup. Please create a new Email Account from Setup > Email > Email Account"
|
||||
),
|
||||
_("Email Account not setup. Please create a new Email Account from Settings > Email Account"),
|
||||
raise_exception=frappe.OutgoingEmailError,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ frappe.ui.form.ControlBarcode = class ControlBarcode extends frappe.ui.form.Cont
|
|||
let svg = value;
|
||||
let barcode_value = "";
|
||||
|
||||
this.set_empty_description();
|
||||
if (value && value.startsWith("<svg")) {
|
||||
barcode_value = $(svg).attr("data-barcode-value");
|
||||
}
|
||||
|
|
@ -44,10 +45,14 @@ frappe.ui.form.ControlBarcode = class ControlBarcode extends frappe.ui.form.Cont
|
|||
if (value) {
|
||||
// Get svg
|
||||
const svg = this.barcode_area.find("svg")[0];
|
||||
JsBarcode(svg, value, this.get_options(value));
|
||||
$(svg).attr("data-barcode-value", value);
|
||||
$(svg).attr("width", "100%");
|
||||
return this.barcode_area.html();
|
||||
try {
|
||||
JsBarcode(svg, value, this.get_options(value));
|
||||
$(svg).attr("data-barcode-value", value);
|
||||
$(svg).attr("width", "100%");
|
||||
return this.barcode_area.html();
|
||||
} catch (e) {
|
||||
this.set_description(`Invalid Barcode: ${String(e)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -208,7 +208,6 @@ body {
|
|||
// Overrides for each widgets
|
||||
&.dashboard-widget-box {
|
||||
min-height: 240px;
|
||||
padding: var(--padding-md) var(--padding-lg);
|
||||
|
||||
.filter-chart {
|
||||
background-color: var(--control-bg);
|
||||
|
|
@ -238,13 +237,16 @@ body {
|
|||
}
|
||||
|
||||
.widget-head {
|
||||
padding: var(--padding-sm);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.widget-body {
|
||||
padding-top: 7px;
|
||||
}
|
||||
|
||||
.widget-control {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
@ -560,7 +562,7 @@ body {
|
|||
}
|
||||
|
||||
&.links-widget-box {
|
||||
padding: 18px 12px;
|
||||
padding: 12px 7px;
|
||||
|
||||
.link-item {
|
||||
display: flex;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import unittest
|
|||
from io import StringIO
|
||||
from unittest.mock import patch
|
||||
|
||||
import git
|
||||
import yaml
|
||||
|
||||
import frappe
|
||||
|
|
@ -134,6 +135,9 @@ class TestBoilerPlate(unittest.TestCase):
|
|||
|
||||
self.check_parsable_python_files(new_app_dir)
|
||||
|
||||
app_repo = git.Repo(new_app_dir)
|
||||
self.assertEqual(app_repo.active_branch.name, "develop")
|
||||
|
||||
def test_create_app_without_git_init(self):
|
||||
app_name = "test_app_no_git"
|
||||
|
||||
|
|
|
|||
|
|
@ -426,12 +426,15 @@ def create_blog_post():
|
|||
return doc
|
||||
|
||||
|
||||
def create_test_user():
|
||||
if frappe.db.exists("User", UI_TEST_USER):
|
||||
@whitelist_for_tests
|
||||
def create_test_user(username=None):
|
||||
name = username or UI_TEST_USER
|
||||
|
||||
if frappe.db.exists("User", name):
|
||||
return
|
||||
|
||||
user = frappe.new_doc("User")
|
||||
user.email = UI_TEST_USER
|
||||
user.email = name
|
||||
user.first_name = "Frappe"
|
||||
user.new_password = frappe.local.conf.admin_password
|
||||
user.send_welcome_email = 0
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ Content,Inhalt,
|
|||
Content Type,Inhaltstyp,
|
||||
Create,Erstellen,
|
||||
Created By,Erstellt von,
|
||||
Crop,Zuschneiden,
|
||||
Current,Laufend,
|
||||
Custom HTML,Benutzerdefiniertes HTML,
|
||||
Custom?,Benutzerdefiniert?,
|
||||
|
|
@ -64,6 +65,7 @@ Delivery Status,Lieferstatus,
|
|||
Department,Abteilung,
|
||||
Details,Details,
|
||||
Document Name,Dokumentenname,
|
||||
Document Naming Settings,Dokumentenbenennungseinstellungen,
|
||||
Document Status,Dokumentenstatus,
|
||||
Document Type,Dokumententyp,
|
||||
Domain,Domäne,
|
||||
|
|
@ -1383,6 +1385,7 @@ Inverse,Invertieren,
|
|||
Is,Ist,
|
||||
Is Attachments Folder,Ist Ordner für Anhänge,
|
||||
Is Child Table,Ist Untertabelle,
|
||||
Is Custom,Ist benutzerdefiniert,
|
||||
Is Custom Field,Ist benutzerdefiniertes Feld,
|
||||
Is First Startup,Ist Erstes Startup,
|
||||
Is Folder,Ist Ordner,
|
||||
|
|
@ -1613,6 +1616,7 @@ Naming,Bezeichnung,
|
|||
Naming Rule, Benennungsregel,
|
||||
"Naming Options:\n<ol><li><b>field:[fieldname]</b> - By Field</li><li><b>naming_series:</b> - By Naming Series (field called naming_series must be present</li><li><b>Prompt</b> - Prompt user for a name</li><li><b>[series]</b> - Series by prefix (separated by a dot); for example PRE.#####</li>\n<li><b>format:EXAMPLE-{MM}morewords{fieldname1}-{fieldname2}-{#####}</b> - Replace all braced words (fieldnames, date words (DD, MM, YY), series) with their value. Outside braces, any characters can be used.</li></ol>","Namensoptionen: <ol><li> <b>Feld: [Feldname]</b> - Nach Feld </li><li> <b>naming_series:</b> - Nach der <b>Namensreihe</b> (das Feld naming_series muss vorhanden sein) </li><li> <b>Eingabeaufforderung</b> - Benutzer nach einem Namen fragen </li><li> <b>[Serie]</b> - Reihe nach Präfix (getrennt durch einen Punkt); zum Beispiel PRE. ##### </li><li> <b>Format: BEISPIEL- {MM} morewords {Feldname1} - {Feldname2} - {#####}</b> - Ersetzt alle verspannten Wörter (Feldnamen, Datumsworte (DD, MM, YY), Serien) durch ihren Wert. Außerhalb von Klammern können beliebige Zeichen verwendet werden. </li></ol>",
|
||||
Naming Series mandatory,Nummernkreis zwingend erforderlich,
|
||||
Navigation Settings,Navigationseinstellungen,
|
||||
Nested set error. Please contact the Administrator.,Schachtelfehler. Bitte den Administrator kontaktieren.,
|
||||
New Activity,Neue Aktivität,
|
||||
New Chat,Neuer Chat,
|
||||
|
|
@ -1720,11 +1724,11 @@ Note: Changing the Page Name will break previous URL to this page.,"Hinweis: Wen
|
|||
Note: Multiple sessions will be allowed in case of mobile device,Hinweis: Mehrere Sitzungen wird im Falle einer mobilen Gerät erlaubt sein,
|
||||
Nothing to show,Nichts anzuzeigen,
|
||||
Nothing to update,Nichts zu aktualisieren,
|
||||
Notification,Mitteilung,
|
||||
Notification,Benachrichtigung,
|
||||
Notification Recipient,Benachrichtigungsempfänger,
|
||||
Notification Tones,Benachrichtigungstöne,
|
||||
Notifications,Benachrichtigungen,
|
||||
Notifications and bulk mails will be sent from this outgoing server.,Hinweise und Massen-E-Mails werden von diesem Postausgangsserver versendet.,
|
||||
Notifications and bulk mails will be sent from this outgoing server.,Benachrichtigungen und Massen-E-Mails werden von diesem Postausgangsserver versendet.,
|
||||
Notify Users On Every Login,Benutzer bei jeder Anmeldung benachrichtigen,
|
||||
Notify if unreplied,"Benachrichtigen, wenn unbeantwortet",
|
||||
Notify if unreplied for (in mins),"Benachrichtigen, wenn unbeantwortet für (in Minuten)",
|
||||
|
|
@ -2323,6 +2327,7 @@ Show more details,Weiteres,
|
|||
Show only errors,Zeige nur Fehler,
|
||||
"Show title in browser window as ""Prefix - title""","Diesen Eintrag im Browser-Fenster als ""Präfix - Titel"" anzeigen",
|
||||
Showing only Numeric fields from Report,Nur numerische Felder aus Bericht anzeigen,
|
||||
Sidebar,Seitenleiste,
|
||||
Sidebar Items,Elemente der Seitenleiste,
|
||||
Sidebar Settings,Sidebar-Einstellungen,
|
||||
Sidebar and Comments,Sidebar und Kommentare,
|
||||
|
|
|
|||
|
|
|
@ -450,12 +450,20 @@ def disable():
|
|||
|
||||
|
||||
@frappe.whitelist()
|
||||
def reset_otp_secret(user):
|
||||
def reset_otp_secret(user: str):
|
||||
if frappe.session.user != user:
|
||||
frappe.only_for("System Manager", message=True)
|
||||
|
||||
otp_issuer = frappe.db.get_single_value("System Settings", "otp_issuer_name")
|
||||
user_email = frappe.db.get_value("User", user, "email")
|
||||
settings = frappe.get_cached_doc("System Settings")
|
||||
|
||||
if not settings.enable_two_factor_auth:
|
||||
frappe.throw(
|
||||
_("You have to enable Two Factor Auth from System Settings."),
|
||||
title=_("Enable Two Factor Auth"),
|
||||
)
|
||||
|
||||
otp_issuer = settings.otp_issuer_name or "Frappe Framework"
|
||||
user_email = frappe.get_cached_value("User", user, "email")
|
||||
|
||||
clear_default(user + "_otplogin")
|
||||
clear_default(user + "_otpsecret")
|
||||
|
|
@ -463,10 +471,10 @@ def reset_otp_secret(user):
|
|||
email_args = {
|
||||
"recipients": user_email,
|
||||
"sender": None,
|
||||
"subject": _("OTP Secret Reset - {0}").format(otp_issuer or "Frappe Framework"),
|
||||
"subject": _("OTP Secret Reset - {0}").format(otp_issuer),
|
||||
"message": _(
|
||||
"<p>Your OTP secret on {0} has been reset. If you did not perform this reset and did not request it, please contact your System Administrator immediately.</p>"
|
||||
).format(otp_issuer or "Frappe Framework"),
|
||||
).format(otp_issuer),
|
||||
"delayed": False,
|
||||
"retry": 3,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1031,8 +1031,13 @@ def groupby_metric(iterable: dict[str, list], key: str):
|
|||
return records
|
||||
|
||||
|
||||
def get_table_name(table_name: str) -> str:
|
||||
return f"tab{table_name}" if not table_name.startswith("__") else table_name
|
||||
def get_table_name(table_name: str, wrap_in_backticks: bool = False) -> str:
|
||||
name = f"tab{table_name}" if not table_name.startswith("__") else table_name
|
||||
|
||||
if wrap_in_backticks:
|
||||
return f"`{name}`"
|
||||
|
||||
return name
|
||||
|
||||
|
||||
def squashify(what):
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ def _create_app_boilerplate(dest, hooks, no_git=False):
|
|||
f.write(frappe.as_unicode(gitignore_template.format(app_name=hooks.app_name)))
|
||||
|
||||
# initialize git repository
|
||||
app_repo = git.Repo.init(app_directory)
|
||||
app_repo = git.Repo.init(app_directory, initial_branch="develop")
|
||||
app_repo.git.add(A=True)
|
||||
app_repo.index.commit("feat: Initialize App")
|
||||
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ dependencies = [
|
|||
"python-dateutil~=2.8.1",
|
||||
"pytz==2022.1",
|
||||
"rauth~=0.7.3",
|
||||
"redis~=4.3.4",
|
||||
"redis~=4.5.4",
|
||||
"hiredis~=2.0.0",
|
||||
"requests-oauthlib~=1.3.0",
|
||||
"requests~=2.27.1",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue