tasks ready

This commit is contained in:
2026-01-22 23:59:16 +03:00
parent 26ba015b75
commit e9d3f3c827
13 changed files with 776 additions and 21 deletions

View File

@@ -22,6 +22,9 @@ Auto-generated from all feature plans. Last updated: 2025-12-19
- SQLite (for job history/results, connection configs), Filesystem (for temporary file uploads) (010-refactor-cli-to-web) - SQLite (for job history/results, connection configs), Filesystem (for temporary file uploads) (010-refactor-cli-to-web)
- Python 3.9+ + FastAPI, Pydantic, requests, pyyaml (migrated from superset_tool) (012-remove-superset-tool) - Python 3.9+ + FastAPI, Pydantic, requests, pyyaml (migrated from superset_tool) (012-remove-superset-tool)
- SQLite (tasks.db, migrations.db), Filesystem (012-remove-superset-tool) - SQLite (tasks.db, migrations.db), Filesystem (012-remove-superset-tool)
- Filesystem (local git repo), SQLite (for GitServerConfig, Environment) (011-git-integration-dashboard)
- Python 3.9+ (Backend), Node.js 18+ (Frontend) + FastAPI, SvelteKit, GitPython (or CLI git), Pydantic, SQLAlchemy, Superset API (011-git-integration-dashboard)
- SQLite (for config/history), Filesystem (local Git repositories) (011-git-integration-dashboard)
- Python 3.9+ (Backend), Node.js 18+ (Frontend Build) (001-plugin-arch-svelte-ui) - Python 3.9+ (Backend), Node.js 18+ (Frontend Build) (001-plugin-arch-svelte-ui)
@@ -42,9 +45,9 @@ cd src; pytest; ruff check .
Python 3.9+ (Backend), Node.js 18+ (Frontend Build): Follow standard conventions Python 3.9+ (Backend), Node.js 18+ (Frontend Build): Follow standard conventions
## Recent Changes ## Recent Changes
- 011-git-integration-dashboard: Added Python 3.9+ (Backend), Node.js 18+ (Frontend) + FastAPI, SvelteKit, GitPython (or CLI git), Pydantic, SQLAlchemy, Superset API
- 011-git-integration-dashboard: Added Python 3.9+, Node.js 18+
- 012-remove-superset-tool: Added Python 3.9+ + FastAPI, Pydantic, requests, pyyaml (migrated from superset_tool) - 012-remove-superset-tool: Added Python 3.9+ + FastAPI, Pydantic, requests, pyyaml (migrated from superset_tool)
- 010-refactor-cli-to-web: Added Python 3.9+ (Backend), Node.js 18+ (Frontend) + FastAPI, SvelteKit, Tailwind CSS, Pydantic, SQLAlchemy, `superset_tool` (internal lib)
- 009-backup-scheduler: Added Python 3.9+, Node.js 18+ + FastAPI, APScheduler, SQLAlchemy, SvelteKit, Tailwind CSS
<!-- MANUAL ADDITIONS START --> <!-- MANUAL ADDITIONS START -->

View File

@@ -0,0 +1,55 @@
slice_name: "FI-0083 \u0421\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043A\u0430\
\ \u043F\u043E \u0414\u0417/\u041F\u0414\u0417"
description: null
certified_by: null
certification_details: null
viz_type: pivot_table_v2
params:
datasource: 859__table
viz_type: pivot_table_v2
slice_id: 4019
groupbyColumns:
- dt
groupbyRows:
- counterparty_search_name
- attribute
time_grain_sqla: P1M
temporal_columns_lookup:
dt: true
metrics:
- m_debt_amount
- m_overdue_amount
metricsLayout: COLUMNS
adhoc_filters:
- clause: WHERE
comparator: No filter
expressionType: SIMPLE
operator: TEMPORAL_RANGE
subject: dt
row_limit: '90000'
order_desc: false
aggregateFunction: Sum
combineMetric: true
valueFormat: SMART_NUMBER
date_format: smart_date
rowOrder: key_a_to_z
colOrder: key_a_to_z
value_font_size: 12
header_font_size: 12
label_align: left
column_config:
m_debt_amount:
d3NumberFormat: ',d'
m_overdue_amount:
d3NumberFormat: ',d'
conditional_formatting: []
extra_form_data: {}
dashboards:
- 184
query_context: '{"datasource":{"id":859,"type":"table"},"force":false,"queries":[{"filters":[{"col":"dt","op":"TEMPORAL_RANGE","val":"No
filter"}],"extras":{"having":"","where":""},"applied_time_extras":{},"columns":[{"timeGrain":"P1M","columnType":"BASE_AXIS","sqlExpression":"dt","label":"dt","expressionType":"SQL"},"counterparty_search_name","attribute"],"metrics":["m_debt_amount","m_overdue_amount"],"orderby":[["m_debt_amount",true]],"annotation_layers":[],"row_limit":90000,"series_limit":0,"order_desc":false,"url_params":{},"custom_params":{},"custom_form_data":{}}],"form_data":{"datasource":"859__table","viz_type":"pivot_table_v2","slice_id":4019,"groupbyColumns":["dt"],"groupbyRows":["counterparty_search_name","attribute"],"time_grain_sqla":"P1M","temporal_columns_lookup":{"dt":true},"metrics":["m_debt_amount","m_overdue_amount"],"metricsLayout":"COLUMNS","adhoc_filters":[{"clause":"WHERE","comparator":"No
filter","expressionType":"SIMPLE","operator":"TEMPORAL_RANGE","subject":"dt"}],"row_limit":"90000","order_desc":false,"aggregateFunction":"Sum","combineMetric":true,"valueFormat":"SMART_NUMBER","date_format":"smart_date","rowOrder":"key_a_to_z","colOrder":"key_a_to_z","value_font_size":12,"header_font_size":12,"label_align":"left","column_config":{"m_debt_amount":{"d3NumberFormat":",d"},"m_overdue_amount":{"d3NumberFormat":",d"}},"conditional_formatting":[],"extra_form_data":{},"dashboards":[184],"force":false,"result_format":"json","result_type":"full"},"result_format":"json","result_type":"full"}'
cache_timeout: null
uuid: 9c293065-73e2-4d9b-a175-d188ff8ef575
version: 1.0.0
dataset_uuid: 9e645dc0-da25-4f61-9465-6e649b0bc4b1

View File

@@ -0,0 +1,13 @@
database_name: Prod Clickhouse
sqlalchemy_uri: clickhousedb+connect://viz_superset_click_prod:XXXXXXXXXX@rgm-s-khclk.hq.root.ad:443/dm
cache_timeout: null
expose_in_sqllab: true
allow_run_async: false
allow_ctas: false
allow_cvas: false
allow_dml: true
allow_file_upload: false
extra:
allows_virtual_table_explore: true
uuid: 97aced68-326a-4094-b381-27980560efa9
version: 1.0.0

View File

@@ -0,0 +1,119 @@
table_name: "FI-0080-06 \u041A\u0430\u043B\u0435\u043D\u0434\u0430\u0440\u044C (\u041E\
\u0431\u0449\u0438\u0439 \u0441\u043F\u0440\u0430\u0432\u043E\u0447\u043D\u0438\u043A\
)"
main_dttm_col: null
description: null
default_endpoint: null
offset: 0
cache_timeout: null
schema: dm_view
sql: "-- [HEADER]\r\n-- [\u041D\u0410\u0417\u041D\u0410\u0427\u0415\u041D\u0418\u0415\
]: \u041F\u043E\u043B\u0443\u0447\u0435\u043D\u0438\u0435 \u0434\u0438\u0430\u043F\
\u0430\u0437\u043E\u043D\u0430 \u0434\u0430\u0442 \u0434\u043B\u044F \u043E\u0442\
\u0447\u0435\u0442\u0430 \u043E \u0437\u0430\u0434\u043E\u043B\u0436\u0435\u043D\
\u043D\u043E\u0441\u0442\u044F\u0445 \u043F\u043E \u043E\u0431\u043E\u0440\u043E\
\u0442\u043D\u044B\u043C \u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0430\u043C\r\
\n-- [\u041A\u041B\u042E\u0427\u0415\u0412\u042B\u0415 \u041A\u041E\u041B\u041E\u041D\
\u041A\u0418]:\r\n-- - from_dt_txt: \u041D\u0430\u0447\u0430\u043B\u044C\u043D\
\u0430\u044F \u0434\u0430\u0442\u0430 \u0432 \u0444\u043E\u0440\u043C\u0430\u0442\
\u0435 DD.MM.YYYY\r\n-- - to_dt_txt: \u041A\u043E\u043D\u0435\u0447\u043D\u0430\
\u044F \u0434\u0430\u0442\u0430 \u0432 \u0444\u043E\u0440\u043C\u0430\u0442\u0435\
\ DD.MM.YYYY\r\n-- [JINJA \u041F\u0410\u0420\u0410\u041C\u0415\u0422\u0420\u042B\
]:\r\n-- - {{ filter_values(\"yes_no_check\") }}: \u0424\u0438\u043B\u044C\u0442\
\u0440 \"\u0414\u0430/\u041D\u0435\u0442\" \u0434\u043B\u044F \u043E\u0433\u0440\
\u0430\u043D\u0438\u0447\u0435\u043D\u0438\u044F \u0432\u044B\u0431\u043E\u0440\u043A\
\u0438 \u043F\u043E \u0434\u0430\u0442\u0435\r\n-- [\u041B\u041E\u0413\u0418\u041A\
\u0410]: \u041E\u043F\u0440\u0435\u0434\u0435\u043B\u044F\u0435\u0442 \u043F\u043E\
\u0440\u043E\u0433\u043E\u0432\u0443\u044E \u0434\u0430\u0442\u0443 \u0432 \u0437\
\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u0438 \u043E\u0442 \u0442\
\u0435\u043A\u0443\u0449\u0435\u0433\u043E \u0434\u043D\u044F \u043C\u0435\u0441\
\u044F\u0446\u0430 \u0438 \u0444\u0438\u043B\u044C\u0442\u0440\u0443\u0435\u0442\
\ \u0434\u0430\u043D\u043D\u044B\u0435\r\n\r\nWITH date_threshold AS (\r\n SELECT\
\ \r\n -- \u041E\u043F\u0440\u0435\u0434\u0435\u043B\u044F\u0435\u043C \u043F\
\u043E\u0440\u043E\u0433\u043E\u0432\u0443\u044E \u0434\u0430\u0442\u0443 \u0432\
\ \u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u0438 \u043E\u0442\
\ \u0442\u0435\u043A\u0443\u0449\u0435\u0433\u043E \u0434\u043D\u044F \r\n \
\ CASE \r\n WHEN toDayOfMonth(now()) <= 10 THEN \r\n \
\ toStartOfMonth(dateSub(MONTH, 1, now())) \r\n ELSE \r\n \
\ toStartOfMonth(now()) \r\n END AS cutoff_date \r\n),\r\nfiltered_dates\
\ AS (\r\n SELECT \r\n dt,\r\n formatDateTime(dt, '%d.%m.%Y') AS\
\ from_dt_txt,\r\n formatDateTime(dt, '%d.%m.%Y') AS to_dt_txt\r\n \
\ --dt as from_dt_txt,\r\n -- dt as to_dt_txt\r\n FROM dm_view.account_debt_for_working_capital_final\r\
\n WHERE 1=1\r\n -- \u0411\u0435\u0437\u043E\u043F\u0430\u0441\u043D\u0430\
\u044F \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u0444\u0438\u043B\u044C\
\u0442\u0440\u0430\r\n {% if filter_values(\"yes_no_check\") | length !=\
\ 0 %}\r\n {% if filter_values(\"yes_no_check\")[0] == \"\u0414\u0430\
\" %}\r\n AND dt < (SELECT cutoff_date FROM date_threshold)\r\n \
\ {% endif %}\r\n {% endif %}\r\n)\r\nSELECT \r\ndt,\r\n from_dt_txt,\r\
\n to_dt_txt,\r\n formatDateTime(toLastDayOfMonth(dt), '%d.%m.%Y') as last_day_of_month_dt_txt\r\
\nFROM \r\n filtered_dates\r\nGROUP BY \r\n dt, from_dt_txt, to_dt_txt\r\n\
ORDER BY \r\n dt DESC"
params: null
template_params: null
filter_select_enabled: true
fetch_values_predicate: null
extra: null
normalize_columns: false
uuid: fca62707-6947-4440-a16b-70cb6a5cea5b
metrics:
- metric_name: max_date
verbose_name: max_date
metric_type: count
expression: max(dt)
description: null
d3format: null
currency: null
extra:
warning_markdown: ''
warning_text: null
columns:
- column_name: from_dt_txt
verbose_name: null
is_dttm: true
is_active: true
type: String
advanced_data_type: null
groupby: true
filterable: true
expression: null
description: null
python_date_format: '%Y'
extra: {}
- column_name: dt
verbose_name: null
is_dttm: true
is_active: true
type: Date
advanced_data_type: null
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
extra: {}
- column_name: last_day_of_month_dt_txt
verbose_name: null
is_dttm: false
is_active: true
type: String
advanced_data_type: null
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
extra: {}
- column_name: to_dt_txt
verbose_name: null
is_dttm: true
is_active: true
type: String
advanced_data_type: null
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
extra: {}
version: 1.0.0
database_uuid: 97aced68-326a-4094-b381-27980560efa9

View File

@@ -0,0 +1,190 @@
table_name: "FI-0090 \u0421\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043A\u0430\
\ \u043F\u043E \u0414\u0417/\u041F\u0414\u0417"
main_dttm_col: dt
description: null
default_endpoint: null
offset: 0
cache_timeout: null
schema: dm_view
sql: "-- [JINJA_BLOCK] \u0426\u0435\u043D\u0442\u0440\u0430\u043B\u0438\u0437\u043E\
\u0432\u0430\u043D\u043D\u043E\u0435 \u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0435\
\u043D\u0438\u0435 \u0432\u0441\u0435\u0445 Jinja \u043F\u0435\u0440\u0435\u043C\
\u0435\u043D\u043D\u044B\u0445\r\n{% set raw_to = filter_values('last_day_of_month_dt_txt')[0]\
\ \r\n if filter_values('last_day_of_month_dt_txt') else '01.05.2025'\
\ %}\r\n\r\n{# \u0440\u0430\u0437\u0431\u0438\u0432\u0430\u0435\u043C \xABDD.MM.YYYY\xBB\
\ \u043D\u0430 \u0447\u0430\u0441\u0442\u0438 #}\r\n{% set to_parts = raw_to.split('.')\
\ %}\r\n\r\n{# \u0441\u043E\u0431\u0438\u0440\u0430\u0435\u043C ISO\u2011\u0441\u0442\
\u0440\u043E\u043A\u0443 \xABYYYY-MM-DD\xBB #}\r\n{% set to_dt = to_parts[2] \
\ ~ '-' ~ to_parts[1] ~ '-' ~ to_parts[0] %}\r\n\r\nwith \r\ncp_relations_type\
\ AS (\r\n select * from ( SELECT \r\n ctd.counterparty_code AS counterparty_code,\r\
\n min(dt_from) as dt_from,\r\n max(dt_to) as dt_to,\r\n crt.relation_type_code\
\ || ' ' || crt.relation_type_name AS relation_type_code_name\r\n FROM\r\n \
\ dm_view.counterparty_td ctd\r\n JOIN dm_view.counterparty_relation_type_texts\
\ crt \r\n ON ctd.relation_type_code = crt.relation_type_code\r\n GROUP\
\ BY\r\n ctd.counterparty_code, ctd.counterparty_full_name,\r\n crt.relation_type_code,crt.relation_type_name)\r\
\n WHERE \r\n dt_from <= toDate('{{to_dt }}') AND \r\n \
\ dt_to >= toDate('{{to_dt }}')\r\n ),\r\nt_debt as \r\n(SELECT dt, \r\n\
counterparty_search_name,\r\ncp_relations_type.relation_type_code_name as relation_type_code_name,\r\
\nunit_balance_code || ' ' || unit_balance_name as unit_balance_code_name,\r\n'1.\
\ \u0421\u0443\u043C\u043C\u0430' as attribute,\r\nsum(debt_balance_subposition_no_revaluation_usd_amount)\
\ as debt_amount,\r\nsumIf(debt_balance_subposition_no_revaluation_usd_amount,dt_overdue\
\ < dt) as overdue_amount\r\nfrom dm_view.account_debt_for_working_capital t_debt\r\
\njoin cp_relations_type ON\r\ncp_relations_type.counterparty_code = t_debt.counterparty_code\r\
\nwhere dt = toLastDayOfMonth(dt)\r\nand match(general_ledger_account_code,'((62)|(60)|(76))')\r\
\nand debit_or_credit = 'S'\r\nand account_type = 'D'\r\nand dt between addMonths(toDate('{{to_dt\
\ }}'),-12) and toDate('{{to_dt }}')\r\ngroup by dt, counterparty_search_name,unit_balance_code_name,relation_type_code_name\r\
\n),\r\n\r\nt_transaction_count_base as \r\n(\r\nselect *,\r\ncp_relations_type.relation_type_code_name\
\ as relation_type_code_name,\r\nunit_balance_code || ' ' || unit_balance_name as\
\ unit_balance_code_name,\r\n case when dt_overdue<dt_clearing then\r\n \
\ dateDiff(day, dt_overdue, dt_clearing) \r\n else 0\r\n end\
\ as overdue_days\r\nfrom dm_view.accounting_documents_leading_to_debt t_docs\r\n\
join cp_relations_type ON\r\ncp_relations_type.counterparty_code = t_docs.counterparty_code\r\
\nwhere 1=1\r\n\r\nand match(general_ledger_account_code,'((62)|(60)|(76))')\r\n\
and debit_or_credit = 'S'\r\nand account_type = 'D'\r\n)\r\n\r\nselect * from t_debt\r\
\n\r\nunion all \r\n\r\nselect toLastDayOfMonth(dt_debt) as dt, \r\ncounterparty_search_name,\r\
\nrelation_type_code_name,\r\nunit_balance_code_name,\r\n'2. \u043A\u043E\u043B\u0438\
\u0447\u0435\u0441\u0442\u0432\u043E \u0442\u0440\u0430\u043D\u0437\u0430\u043A\u0446\
\u0438\u0439 \u0432 \u043C\u0435\u0441\u044F\u0446' as attribute,\r\ncount(1) as\
\ debt_amount,\r\nnull as overdue_amount\r\nfrom t_transaction_count_base\r\nwhere\
\ dt_debt between addMonths(toDate('{{to_dt }}'),-12) and toDate('{{to_dt }}')\r\
\ngroup by toLastDayOfMonth(dt_debt), \r\ncounterparty_search_name,\r\nrelation_type_code_name,\r\
\nunit_balance_code_name,attribute\r\n\r\nunion all \r\n\r\nselect toLastDayOfMonth(dt_clearing)\
\ as dt, \r\ncounterparty_search_name,\r\nrelation_type_code_name,\r\nunit_balance_code_name,\r\
\n'2. \u043A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E \u0442\u0440\u0430\
\u043D\u0437\u0430\u043A\u0446\u0438\u0439 \u0432 \u043C\u0435\u0441\u044F\u0446\
' as attribute,\r\nnull as debt_amount,\r\ncount(1) as overdue_amount\r\nfrom t_transaction_count_base\r\
\nwhere dt_clearing between addMonths(toDate('{{to_dt }}'),-12) and toDate('{{to_dt\
\ }}')\r\nand overdue_days > 0\r\ngroup by toLastDayOfMonth(dt_clearing), \r\ncounterparty_search_name,\r\
\nrelation_type_code_name,\r\nunit_balance_code_name,attribute\r\n\r\nunion all\
\ \r\n\r\nselect toLastDayOfMonth(dt_clearing) as dt, \r\ncounterparty_search_name,\r\
\nrelation_type_code_name,\r\nunit_balance_code_name,\r\nmultiIf(\r\noverdue_days\
\ < 30,'3. \u0434\u043E 30',\r\noverdue_days between 30 and 60, '4. \u043E\u0442\
\ 30 \u0434\u043E 60',\r\noverdue_days between 61 and 90, '5. \u043E\u0442 61 \u0434\
\u043E 90',\r\noverdue_days>90,'6. \u0431\u043E\u043B\u0435\u0435 90 \u0434\u043D\
',\r\nnull\r\n)\r\n as attribute,\r\nnull as debt_amount,\r\ncount(1) as overdue_amount\r\
\nfrom t_transaction_count_base\r\nwhere dt_clearing between addMonths(toDate('{{to_dt\
\ }}'),-12) and toDate('{{to_dt }}')\r\nand overdue_days > 0\r\ngroup by toLastDayOfMonth(dt_clearing),\
\ \r\ncounterparty_search_name,\r\nrelation_type_code_name,\r\nattribute,unit_balance_code_name,attribute\r\
\n"
params: null
template_params: null
filter_select_enabled: true
fetch_values_predicate: null
extra: null
normalize_columns: false
uuid: 9e645dc0-da25-4f61-9465-6e649b0bc4b1
metrics:
- metric_name: m_debt_amount
verbose_name: "\u0414\u0417, $"
metric_type: count
expression: sum(debt_amount)
description: null
d3format: null
currency: null
extra:
warning_markdown: ''
warning_text: null
- metric_name: m_overdue_amount
verbose_name: "\u041F\u0414\u0417, $"
metric_type: null
expression: sum(overdue_amount)
description: null
d3format: null
currency: null
extra:
warning_markdown: ''
warning_text: null
columns:
- column_name: debt_amount
verbose_name: null
is_dttm: false
is_active: true
type: Nullable(Decimal(38, 2))
advanced_data_type: null
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
extra:
warning_markdown: null
- column_name: overdue_amount
verbose_name: null
is_dttm: false
is_active: true
type: Nullable(Decimal(38, 2))
advanced_data_type: null
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
extra:
warning_markdown: null
- column_name: dt
verbose_name: null
is_dttm: true
is_active: true
type: Nullable(Date)
advanced_data_type: null
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
extra:
warning_markdown: null
- column_name: unit_balance_code_name
verbose_name: null
is_dttm: false
is_active: true
type: Nullable(String)
advanced_data_type: null
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
extra:
warning_markdown: null
- column_name: relation_type_code_name
verbose_name: null
is_dttm: false
is_active: true
type: Nullable(String)
advanced_data_type: null
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
extra:
warning_markdown: null
- column_name: counterparty_search_name
verbose_name: null
is_dttm: false
is_active: true
type: Nullable(String)
advanced_data_type: null
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
extra:
warning_markdown: null
- column_name: attribute
verbose_name: null
is_dttm: false
is_active: true
type: Nullable(String)
advanced_data_type: null
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
extra:
warning_markdown: null
version: 1.0.0
database_uuid: 97aced68-326a-4094-b381-27980560efa9

View File

@@ -0,0 +1,3 @@
version: 1.0.0
type: Dashboard
timestamp: '2026-01-14T11:21:08.078620+00:00'

View File

@@ -0,0 +1,58 @@
# API Contracts: Git Integration Plugin
## Git Configuration
### `GET /api/git/config`
List all Git server configurations.
### `POST /api/git/config`
Create a new Git server configuration.
- **Body**: `GitServerConfig` (Pydantic model)
### `POST /api/git/config/test`
Test connection to a Git server.
- **Body**: `GitServerConfig`
- **Response**: `{"status": "success" | "error", "message": String}`
## Repository & Branch Management
### `GET /api/git/repositories/{dashboard_id}/branches`
List all branches for a dashboard's repository.
### `POST /api/git/repositories/{dashboard_id}/branches`
Create a new branch.
- **Body**: `{"name": String, "from_branch": String}`
### `POST /api/git/repositories/{dashboard_id}/checkout`
Switch to a specific branch.
- **Body**: `{"name": String}`
## Git Operations
### `POST /api/git/repositories/{dashboard_id}/commit`
Commit changes to the current branch.
- **Body**: `{"message": String, "files": List[String]}`
### `POST /api/git/repositories/{dashboard_id}/push`
Push local commits to remote.
### `POST /api/git/repositories/{dashboard_id}/pull`
Pull changes from remote.
## Conflict Resolution
### `GET /api/git/repositories/{dashboard_id}/conflicts`
List active conflicts for a repository.
### `POST /api/git/repositories/{dashboard_id}/resolve`
Resolve a conflict for a specific file.
- **Body**: `{"file_path": String, "resolution": "mine" | "theirs" | "manual", "content": Optional[String]}`
## Deployment
### `GET /api/git/environments`
List deployment environments.
### `POST /api/git/repositories/{dashboard_id}/deploy`
Deploy dashboard from current branch to target environment.
- **Body**: `{"environment_id": UUID}`

View File

@@ -0,0 +1,56 @@
# Data Model: Git Integration Plugin
## Entities
### GitServerConfig
- **id**: UUID (Primary Key)
- **name**: String (Display name)
- **provider**: Enum (GITHUB, GITLAB, GITEA)
- **url**: String (e.g., https://github.com)
- **pat**: String (Encrypted/Sensitive)
- **default_repository**: String (e.g., org/dashboards)
- **status**: Enum (CONNECTED, FAILED, UNKNOWN)
- **last_validated**: DateTime
### GitRepository
- **id**: UUID (Primary Key)
- **dashboard_id**: Integer (Link to Superset Dashboard ID)
- **config_id**: UUID (FK to GitServerConfig)
- **remote_url**: String
- **local_path**: String (Relative to backend storage)
- **current_branch**: String
- **sync_status**: Enum (CLEAN, DIRTY, CONFLICT)
### Branch
- **name**: String
- **commit_hash**: String
- **is_remote**: Boolean
- **last_updated**: DateTime
### Commit
- **hash**: String (Primary Key)
- **author**: String
- **email**: String
- **timestamp**: DateTime
- **message**: String
- **files_changed**: List[String]
### Conflict
- **repository_id**: UUID (FK to GitRepository)
- **file_path**: String
- **our_content**: String
- **their_content**: String
- **base_content**: String
### DeploymentEnvironment
- **id**: UUID (Primary Key)
- **name**: String (e.g., Production, Staging)
- **superset_url**: String
- **superset_token**: String (Sensitive)
- **is_active**: Boolean
## Relationships
- **GitServerConfig** (1) <-> (*) **GitRepository**
- **GitRepository** (1) <-> (*) **Branch**
- **Branch** (1) <-> (*) **Commit**
- **GitRepository** (1) <-> (*) **DeploymentEnvironment** (Via deployment logs/config)

View File

@@ -0,0 +1,102 @@
# Implementation Plan: Git Integration Plugin for Dashboard Development
**Branch**: `011-git-integration-dashboard` | **Date**: 2026-01-22 | **Spec**: [spec.md](specs/011-git-integration-dashboard/spec.md)
**Input**: Feature specification from `/specs/011-git-integration-dashboard/spec.md`
## Summary
Implement a Git integration plugin that allows dashboard developers to version control their Superset dashboards. The plugin will support GitLab, GitHub, and Gitea, enabling branch management, committing/pushing changes (as unpacked Superset exports), and deploying to target environments via the Superset API.
## Technical Context
**Language/Version**: Python 3.9+ (Backend), Node.js 18+ (Frontend)
**Primary Dependencies**: FastAPI, SvelteKit, GitPython (or CLI git), Pydantic, SQLAlchemy, Superset API
**Storage**: SQLite (for config/history), Filesystem (local Git repositories)
**Testing**: pytest (Backend), SvelteKit testing utilities (Frontend)
**Target Platform**: Linux server
**Project Type**: Web application (Frontend + Backend)
**Performance Goals**: Branch switching < 5s, Search/Filter history < 2s
**Constraints**: Offline mode support, Superset API dependency for deployment, PAT-based authentication
**Scale/Scope**: 1 repository per dashboard, support for up to 1000 commits per repo
## Constitution Check
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
1. **Semantic Protocol Compliance**: All new modules must include headers, `[DEF]` anchors, and `@RELATION` tags as per `semantic_protocol.md`.
2. **Causal Validity**: API contracts and data models must be defined in `specs/` before implementation.
3. **Everything is a Plugin**: The Git integration must be implemented as a `PluginBase` subclass in `backend/src/plugins/`.
4. **Design by Contract**: Use Pydantic models for request/response validation and internal state transitions.
## Project Structure
### Documentation (this feature)
```text
specs/[###-feature]/
├── plan.md # This file (/speckit.plan command output)
├── research.md # Phase 0 output (/speckit.plan command)
├── data-model.md # Phase 1 output (/speckit.plan command)
├── quickstart.md # Phase 1 output (/speckit.plan command)
├── contracts/ # Phase 1 output (/speckit.plan command)
└── tasks.md # Phase 2 output (/speckit.tasks command - NOT created by /speckit.plan)
```
### Source Code (repository root)
<!--
ACTION REQUIRED: Replace the placeholder tree below with the concrete layout
for this feature. Delete unused options and expand the chosen structure with
real paths (e.g., apps/admin, packages/something). The delivered plan must
not include Option labels.
-->
```text
backend/
├── src/
│ ├── api/routes/git.py # Git integration endpoints
│ ├── models/git.py # Git-specific Pydantic/SQLAlchemy models
│ ├── plugins/git_plugin.py # PluginBase implementation
│ └── services/git_service.py # Core Git logic (GitPython wrapper)
└── tests/
└── plugins/test_git.py
frontend/
├── src/
│ ├── components/git/ # Git UI components (BranchSelector, CommitModal, ConflictResolver)
│ ├── routes/settings/git/ # Git configuration pages
│ └── services/gitService.js # API client for Git operations
└── tests/
**Structure Decision**: Web application structure as the project has both FastAPI backend and SvelteKit frontend.
## Complexity Tracking
> **Fill ONLY if Constitution Check has violations that must be justified**
| Violation | Why Needed | Simpler Alternative Rejected Because |
|-----------|------------|-------------------------------------|
| [e.g., 4th project] | [current need] | [why 3 projects insufficient] |
[e.g., Repository pattern] | [specific problem] | [why direct DB access insufficient] |
## Implementation Phases
### Phase 2: Backend Implementation (Plugin & Service)
1. **Git Service**: Implement `GitService` using `GitPython`. Focus on:
* Repo initialization and cloning.
* Branch management (list, create, checkout).
* Stage, commit, push, pull.
2. **Git Plugin**: Implement `GitPlugin(PluginBase)`.
* Integrate with `superset_tool` for exporting/importing dashboards.
* Handle unpacking ZIP exports into the repo structure.
3. **API Routes**: Implement `/api/git/*` endpoints.
### Phase 3: Frontend Implementation
1. **Configuration UI**: Settings page for `GitServerConfig`.
2. **Dashboard Integration**: Add Git controls to the Dashboard view.
* Branch selector.
* Commit/Push/Pull buttons.
3. **Conflict Resolution UI**: Implementation of "Keep Mine/Theirs" file picker.
### Phase 4: Deployment Integration
1. **Environment Management**: CRUD for `DeploymentEnvironment`.
2. **Deployment Logic**: Trigger Superset Import API on target servers.

View File

@@ -0,0 +1,40 @@
# Quickstart: Git Integration Plugin
## Setup
1. **Install Dependencies**:
```bash
cd backend && .venv/bin/pip install GitPython
```
2. **Configure Git Server**:
- Go to Settings -> Git Integration
- Click "Add Server"
- Select Provider (e.g., GitLab)
- Enter Server URL and Personal Access Token (PAT)
- Click "Test Connection" and "Save"
3. **Link Dashboard to Git**:
- Navigate to the Dashboard view
- Select a dashboard
- Click "Enable Git Integration"
- Select the Git server and provide repository path (e.g., `my-org/my-dashboard-repo`)
## Common Workflows
### Versioning Changes
1. Make changes to the dashboard in Superset.
2. In the Git Integration panel, click "Commit".
3. Enter a commit message and select files (metadata, charts, etc.).
4. Click "Push" to sync with remote.
### Branching
1. Click "New Branch".
2. Enter branch name (e.g., `feature/new-charts`).
3. The dashboard state is now tracked on this branch.
### Deployment
1. Ensure changes are committed and pushed.
2. Click "Deploy".
3. Select target environment (e.g., "Production").
4. Monitor deployment logs for success.

View File

@@ -0,0 +1,34 @@
# Research: Git Integration Plugin
## Unknowns & Clarifications
1. **Git Library**: Should we use `GitPython` or call `git` CLI directly?
- **Decision**: Use `GitPython`.
- **Rationale**: It provides a high-level API for Git operations, making it easier to handle branches, commits, and remotes programmatically compared to parsing CLI output.
- **Alternatives considered**: `pygit2` (faster but requires libgit2), `subprocess.run(["git", ...])` (simple but brittle).
2. **Git Provider API Integration**: How to handle different providers (GitHub, GitLab, Gitea) for connection testing?
- **Decision**: Use a unified provider interface with provider-specific implementations.
- **Rationale**: While Git operations are standard, testing connections and potentially creating repositories might require provider-specific REST APIs.
- **Alternatives considered**: Generic Git clone test (slower, requires local disk space).
3. **Superset Export/Import**: How to handle the "unpacked" requirement?
- **Decision**: Use the existing `superset_tool` logic or similar to export as ZIP and then unpack to the Git directory.
- **Rationale**: Superset's native export is a ZIP. To fulfill FR-019, we must unpack this ZIP into the repository structure.
4. **Merge Conflict UI**: How to implement "Keep Mine/Theirs" in a web UI?
- **Decision**: Implement a file-based conflict resolver in the Frontend.
- **Rationale**: Since the data is YAML, we can show a list of conflicting files and let the user pick the version.
- **Alternatives considered**: Full 3-way merge UI (too complex for MVP).
## Best Practices
- **Security**: Never store PATs in plain text in logs. Use environment variables or encrypted storage if possible (though SQLite is the current project standard).
- **Concurrency**: Git operations on the same repository should be serialized to avoid index locks.
- **Repository Isolation**: Each dashboard gets its own directory/repository to prevent cross-dashboard pollution.
## Findings Consolidations
- **Decision**: Use `GitPython` for core Git logic.
- **Decision**: Use Provider-specific API clients for connection validation.
- **Decision**: Unpack Superset exports into `dashboard/`, `charts/`, `datasets/`, `databases/` directories.

View File

@@ -22,16 +22,16 @@
### User Story 1 - Configure Git Server Connection (Priority: P1) ### User Story 1 - Configure Git Server Connection (Priority: P1)
A dashboard developer needs to connect the system to their GitLab server to enable version control for dashboard development. They want to configure the Git server URL, authentication credentials, and repository details through a simple form interface. A dashboard developer needs to connect the system to their Git server (GitLab, GitHub, or Gitea) to enable version control for dashboard development. They want to configure the Git server provider, URL, authentication credentials, and repository details through a simple form interface.
**Why this priority**: This is the foundational requirement - without Git server configuration, no other Git functionality can work. It's the entry point for all Git operations. **Why this priority**: This is the foundational requirement - without Git server configuration, no other Git functionality can work. It's the entry point for all Git operations.
**Independent Test**: Can be fully tested by configuring a GitLab server connection and verifying the connection test succeeds, delivering the ability to establish Git server connectivity. **Independent Test**: Can be fully tested by configuring a Git server connection (e.g., GitHub) and verifying the connection test succeeds, delivering the ability to establish Git server connectivity.
**Acceptance Scenarios**: **Acceptance Scenarios**:
1. **Given** a user is on the Git integration settings page, **When** they enter valid GitLab server URL and API token, **Then** the system successfully validates the connection 1. **Given** a user is on the Git integration settings page, **When** they select a provider (GitLab/GitHub/Gitea) and enter valid server URL and PAT, **Then** the system successfully validates the connection
2. **Given** a user enters invalid GitLab credentials, **When** they test the connection, **Then** the system displays a clear error message indicating authentication failure 2. **Given** a user enters invalid Git credentials, **When** they test the connection, **Then** the system displays a clear error message indicating authentication failure
3. **Given** a user has configured a Git server, **When** they save the settings, **Then** the configuration is persisted and can be retrieved later 3. **Given** a user has configured a Git server, **When** they save the settings, **Then** the configuration is persisted and can be retrieved later
--- ---
@@ -113,25 +113,28 @@ A dashboard developer needs to view the commit history and changes made to dashb
## Clarifications ## Clarifications
### Session 2026-01-18
- Q: What is the primary data format for storing dashboards in the Git repository? → A: YAML files (more human-readable for diffs) ### Session 2026-01-22
- Q: How should the system handle merge conflicts during synchronization? → A: Built-in UI for basic resolution (Mine/Theirs/Manual) - Q: Что именно должно входить в состав данных дашборда в Git-репозитории? → A: Распакованный архив экспорта Superset (YAML файлы: метаданные, чарты, датасеты, базы данных).
- Q: Какова структура хранения дашбордов в Git-репозитории? → A: Один репозиторий соответствует одному дашборду.
- Q: Как должен происходить деплой на целевое окружение после коммита? → A: Импорт через API (Superset Import API).
- Q: Как система должна обрабатывать конфликты слияния (merge conflicts)? → A: UI для выбора версии (Keep Mine / Keep Theirs) на уровне файлов.
- Q: Как должен происходить выбор дашборда для работы с Git? → A: Выбор конкретного дашборда из списка в UI.
- Q: What triggers a deployment to a target environment? → A: Manual trigger by user from UI - Q: What triggers a deployment to a target environment? → A: Manual trigger by user from UI
- Q: How should the system handle authentication with GitLab? → A: Personal Access Token (PAT) - Q: How should the system handle authentication for the different providers (GitHub, GitLab, Gitea)? → A: Option [A] - Personal Access Token (PAT) for all providers
- Q: What should be the scope of a "Dashboard" in the Git repository? → A: Directory per dashboard (metadata + assets)
### Functional Requirements ### Functional Requirements
- **FR-001**: System MUST allow users to configure Git server connection settings including server URL, authentication via Personal Access Token (PAT), and repository details - **FR-001**: System MUST allow users to configure Git server connection settings including provider type (GitHub, GitLab, Gitea), server URL, authentication via Personal Access Token (PAT), and repository details
- **FR-002**: System MUST support GitLab as the primary Git server with extensible architecture for other Git servers - **FR-002**: System MUST support GitLab, GitHub, and Gitea as supported Git server providers
- **FR-003**: System MUST validate Git server connection using the provided PAT before saving configuration - **FR-003**: System MUST validate Git server connection using the provided PAT for the selected provider before saving configuration
- **FR-004**: Users MUST be able to view all available branches from the configured Git repository - **FR-004**: Users MUST be able to view all available branches from the configured Git repository
- **FR-005**: Users MUST be able to create new branches both locally and remotely with proper naming validation - **FR-005**: Users MUST be able to create new branches both locally and remotely with proper naming validation
- **FR-006**: Users MUST be able to switch between existing branches and have dashboard content update accordingly - **FR-006**: Users MUST be able to switch between existing branches and have dashboard content update accordingly
- **FR-007**: System MUST allow users to commit dashboard changes with commit messages and optional file selection - **FR-007**: System MUST allow users to commit dashboard changes with commit messages and optional file selection
- **FR-008**: System MUST support pushing local commits to remote repository branches - **FR-008**: System MUST support pushing local commits to remote repository branches
- **FR-009**: System MUST support pulling changes from remote repository to local working directory - **FR-009**: System MUST support pulling changes from remote repository to local working directory
- **FR-010**: System MUST handle merge conflicts with a built-in UI providing "Keep Mine", "Keep Theirs", and "Manual Edit" resolution options - **FR-010**: System MUST handle merge conflicts with a built-in UI providing "Keep Mine", "Keep Theirs", and "Manual Edit" (via a text area editor for YAML content) resolution options
- **FR-011**: Users MUST be able to configure multiple target environments for dashboard deployment - **FR-011**: Users MUST be able to configure multiple target environments for dashboard deployment
- **FR-012**: System MUST allow users to manually trigger deployment to a selected target environment from the UI - **FR-012**: System MUST allow users to manually trigger deployment to a selected target environment from the UI
- **FR-013**: System MUST validate deployment configuration before initiating deployment process - **FR-013**: System MUST validate deployment configuration before initiating deployment process
@@ -140,7 +143,7 @@ A dashboard developer needs to view the commit history and changes made to dashb
- **FR-016**: System MUST display detailed changes included in each commit for audit purposes - **FR-016**: System MUST display detailed changes included in each commit for audit purposes
- **FR-017**: System MUST handle PAT expiration gracefully with re-authentication prompts - **FR-017**: System MUST handle PAT expiration gracefully with re-authentication prompts
- **FR-018**: System MUST provide offline mode functionality when Git server is unavailable - **FR-018**: System MUST provide offline mode functionality when Git server is unavailable
- **FR-019**: System MUST validate dashboard files before committing to ensure they are properly formatted in YAML within their respective dashboard directories - **FR-019**: System MUST store and validate dashboard data as a standard unpacked Superset export (YAML files for dashboard metadata, charts, datasets, and database connections) within their respective directories.
- **FR-020**: Users MUST be able to search and filter commit history by date, author, or message content - **FR-020**: Users MUST be able to search and filter commit history by date, author, or message content
- **FR-021**: System MUST support rollback functionality to previous dashboard versions via Git operations - **FR-021**: System MUST support rollback functionality to previous dashboard versions via Git operations
@@ -151,16 +154,12 @@ A dashboard developer needs to view the commit history and changes made to dashb
- **Commit**: Represents a Git commit with properties like commit hash, author, timestamp, commit message, and list of changed files - **Commit**: Represents a Git commit with properties like commit hash, author, timestamp, commit message, and list of changed files
- **Environment**: Represents a deployment target environment with properties like name, URL, authentication details, and deployment status - **Environment**: Represents a deployment target environment with properties like name, URL, authentication details, and deployment status
- **DashboardChange**: Represents changes made to dashboard directories (YAML metadata + assets) including file paths, change type (add/modify/delete), and content differences - **DashboardChange**: Represents changes made to dashboard directories (YAML metadata + assets) including file paths, change type (add/modify/delete), and content differences
- **Branch**: Represents a Git branch with properties like name, commit hash, last updated timestamp, and remote tracking status
- **Commit**: Represents a Git commit with properties like commit hash, author, timestamp, commit message, and list of changed files
- **Environment**: Represents a deployment target environment with properties like name, URL, authentication details, and deployment status
- **DashboardChange**: Represents changes made to dashboard files (YAML format) including file paths, change type (add/modify/delete), and content differences
## Success Criteria *(mandatory)* ## Success Criteria *(mandatory)*
### Measurable Outcomes ### Measurable Outcomes
- **SC-001**: Users can successfully configure a GitLab server connection and validate it within 2 minutes - **SC-001**: Users can successfully configure a supported Git server connection (GitLab, GitHub, or Gitea) and validate it within 2 minutes
- **SC-002**: Dashboard branch switching operations complete within 5 seconds for repositories with up to 100 commits - **SC-002**: Dashboard branch switching operations complete within 5 seconds for repositories with up to 100 commits
- **SC-003**: Commit and push operations succeed in 95% of attempts under normal network conditions - **SC-003**: Commit and push operations succeed in 95% of attempts under normal network conditions
- **SC-004**: Deployment to target environments completes successfully within 30 seconds for standard dashboard configurations - **SC-004**: Deployment to target environments completes successfully within 30 seconds for standard dashboard configurations

View File

@@ -0,0 +1,83 @@
# Tasks: Git Integration Plugin for Dashboard Development
Feature: 011-git-integration-dashboard
## Phase 1: Setup
Goal: Initialize project structure and dependencies.
- [ ] T001 Add `GitPython` to `backend/requirements.txt`
- [ ] T002 Create git plugin directory structure in `backend/src/plugins/git/` and `frontend/src/components/git/`
## Phase 2: Foundational
Goal: Define data models and base classes for Git integration.
- [ ] T003 [P] Create SQLAlchemy models for `GitServerConfig` and `GitRepository` in `backend/src/models/git.py`
- [ ] T004 [P] Create Pydantic schemas for Git entities in `backend/src/api/routes/git_schemas.py`
- [ ] T005 Implement `GitService` base logic (init, clone) in `backend/src/services/git_service.py`
- [ ] T006 Implement `GitPlugin(PluginBase)` skeleton in `backend/src/plugins/git_plugin.py`
## Phase 3: User Story 1 - Configure Git Server Connection (P1)
Goal: Enable connection to Git servers (GitHub, GitLab, Gitea) via PAT.
Independent Test: Configure a Git server connection and verify "Test Connection" succeeds.
- [ ] T007 [US1] Implement Git provider connection validation logic in `backend/src/services/git_service.py`
- [ ] T008 [US1] Implement `/api/git/config` and `/api/git/config/test` endpoints in `backend/src/api/routes/git.py`
- [ ] T009 [P] [US1] Create Git configuration service in `frontend/src/services/gitService.js`
- [ ] T010 [US1] Implement Git server settings page in `frontend/src/routes/settings/git/+page.svelte`
## Phase 4: User Story 2 - Dashboard Branch Management (P1)
Goal: Manage branches for dashboard repositories.
Independent Test: Create a new branch, switch to it, and verify the local repository state updates.
- [ ] T011 [US2] Implement branch listing and creation logic in `backend/src/services/git_service.py`
- [ ] T012 [US2] Implement branch checkout logic in `backend/src/services/git_service.py`
- [ ] T013 [US2] Implement branch management endpoints in `backend/src/api/routes/git.py`
- [ ] T014 [P] [US2] Create `BranchSelector` component in `frontend/src/components/git/BranchSelector.svelte`
- [ ] T015 [US2] Integrate branch management into Dashboard view in `frontend/src/pages/Dashboard.svelte`
## Phase 5: User Story 3 - Dashboard Synchronization with Git (P1)
Goal: Commit, push, and pull dashboard changes.
Independent Test: Modify a dashboard, commit changes, push to remote, and verify changes on the Git server.
- [ ] T016 [US3] Implement Superset export unpacking logic (YAML files) in `backend/src/plugins/git_plugin.py`
- [ ] T017 [US3] Implement commit, push, and pull logic in `backend/src/services/git_service.py`
- [ ] T018 [US3] Implement sync endpoints (commit, push, pull) in `backend/src/api/routes/git.py`
- [ ] T019 [P] [US3] Create `CommitModal` component in `frontend/src/components/git/CommitModal.svelte`
- [ ] T020 [US3] Implement sync controls (Commit/Push/Pull) in `frontend/src/pages/Dashboard.svelte`
- [ ] T021 [US3] Implement conflict detection and resolution logic in `backend/src/services/git_service.py`
- [ ] T022 [US3] Implement conflict resolution endpoints in `backend/src/api/routes/git.py`
- [ ] T023 [P] [US3] Create `ConflictResolver` component in `frontend/src/components/git/ConflictResolver.svelte`
## Phase 6: User Story 4 - Environment Deployment (P2)
Goal: Deploy dashboard changes to target environments.
Independent Test: Select a target environment and trigger deployment, verifying the dashboard is updated on the target Superset instance.
- [ ] T024 [P] [US4] Implement `DeploymentEnvironment` model in `backend/src/models/git.py`
- [ ] T025 [US4] Implement deployment logic (Superset Import API) in `backend/src/plugins/git_plugin.py`
- [ ] T026 [US4] Implement deployment endpoints in `backend/src/api/routes/git.py`
- [ ] T027 [US4] Create environment management UI in `frontend/src/routes/settings/environments/+page.svelte`
- [ ] T028 [US4] Implement deployment trigger in `frontend/src/pages/Dashboard.svelte`
## Phase 7: User Story 5 - Git History and Change Tracking (P3)
Goal: View dashboard commit history and changes.
Independent Test: Open the history view and verify the list of commits matches the Git repository history.
- [ ] T029 [US5] Implement commit history retrieval in `backend/src/services/git_service.py`
- [ ] T030 [US5] Implement history endpoint in `backend/src/api/routes/git.py`
- [ ] T031 [P] [US5] Create `CommitHistory` component in `frontend/src/components/git/CommitHistory.svelte`
## Phase 8: Polish & Cross-cutting Concerns
- [ ] T032 Add error handling and loading states to all Git UI components
- [ ] T033 Implement offline mode checks for Git operations
- [ ] T034 Final integration testing of the complete Git workflow
## Dependencies
- US1 is a prerequisite for all other stories.
- US2 and US3 are closely related; US2 (branches) should precede US3 (sync).
- US4 depends on US3 (changes must be committed to be deployed).
- US5 is independent but requires commits to exist.
## Implementation Strategy
1. **MVP**: Complete Phase 1-3 (Setup, Foundational, and US1) to allow Git server configuration.
2. **Core Workflow**: Complete Phase 4-5 (Branches and Sync) to enable version control.
3. **Extension**: Complete Phase 6-7 (Deployment and History).