Tetapi kami menemukan jalan keluar - sekarang tidak mungkin untuk berkomitmen ke repositori tanpa tes. Setidaknya tanpa disadari dan dengan impunitas.
Sistem pengujian
Hal pertama yang kita butuhkan adalah sistem pengujian. Kami sudah menjelaskannya di sini. Ingatlah bahwa Anda memerlukan kode yang sama untuk dijalankan di server Ci, dan secara lokal, sehingga tidak ada kesulitan dalam pemeliharaan. Diharapkan bahwa proyek dapat mengatur berbagai parameter untuk pengujian umum, atau bahkan lebih baik - memperluasnya sendiri. Tentu saja, permen itu tidak langsung keluar.
Tahap satu- kamu bisa lari, tapi sakit. Apa yang harus dilakukan dengan kode python masih jelas, tetapi dengan semua jenis utilitas seperti CppCheck, Bloaty, optipng, kruk internal kami, sepeda - tidak. Untuk berjalan dengan benar, kami membutuhkan file yang dapat dieksekusi untuk semua platform tempat rekan kerja kami bekerja (mac, windows dan linux). Pada tahap ini, semua biner yang diperlukan ada di repositori, dan jalur relatif ke folder biner ditunjukkan dalam pengaturan sistem pengujian.
<CppCheck bin_folder=βutils/cppcheckβ>...</CppCheck>
Ini menimbulkan beberapa masalah:
- dari sisi proyek, Anda perlu menyimpan file yang tidak diperlukan di repositori, karena file tersebut dibutuhkan di komputer masing-masing pengembang. Secara alami, repositori lebih besar karena ini.
- ketika masalah muncul, sulit untuk memahami versi mana yang dimiliki proyek, apakah struktur yang diperlukan ada di folder.
- di mana mendapatkan binari yang diperlukan? Kompilasi sendiri, unduh di Internet?
Tahap dua - kami mengatur segala sesuatunya dalam utilitas. Tetapi bagaimana jika Anda menulis semua utilitas yang diperlukan dan mengumpulkannya dalam satu repositori? Idenya adalah bahwa di server sudah ada utilitas yang dirakit untuk semua platform yang diperlukan, yang juga berversi. Kami sudah menggunakan Nexus Sonatype, jadi kami pergi ke departemen berikutnya dan menyetujui file. Hasilnya adalah sebuah struktur:
Untuk memulai, Anda memerlukan skrip yang mengetahui alamat rahasia di mana binari berada, dapat mengunduhnya, dan juga menjalankan, bergantung pada platformnya, dengan parameter yang diteruskan.
Menghilangkan kerumitan implementasi
def get_tools_info(project_tools_xml, available_tools_xml): # Parse available tools at first and feel up dictionary root = etree.parse(available_tools_xml).getroot() tools = {} # Parse xml and find current installed version ... return tools def update_tool(tool_info: ToolInfo): if tool_info.current_version == tool_info.needed_version: return if tool_info.needed_version not in tool_info.versions: raise RuntimeError(f'Tool "{tool_info.tool_id}" has no version "{tool_info.needed_version}"') if os.path.isdir(tool_info.output_folder): shutil.rmtree(tool_info.output_folder) g_server_interface.download(tool_id=tool_info.tool_id, version=tool_info.needed_version, output_folder=tool_info.output_folder) def run_tool(tool_info: ToolInfo, tool_args): system_name = platform.system().lower() tool_bin = tool_info.exe_infos[system_name].executable full_path = os.path.join(tool_info.output_folder, tool_bin) command = [full_path] + tool_args try: print(f'Run tool: "{tool_info.tool_id}" with commands: "{" ".join(tool_args)}"') output = subprocess.check_output(command) print(output) except Exception as e: print(f'Fail with: {e}') return 1 return 0 def run(project_tools_xml, available_tools_xml, tool_id, tool_args): tools = get_tools_info(project_tools_xml=project_tools_xml, available_tools_xml=available_tools_xml) update_tool(tools[tool_id]) return run_tool(tool_info, tool_args)
Di server, kami menambahkan file dengan deskripsi utilitas. Alamat file ini tidak berubah, jadi hal pertama yang kami lakukan adalah pergi ke sana dan melihat apa yang kami miliki. Menghilangkan kehalusan, ini adalah nama paket dan jalur ke file yang dapat dieksekusi di dalam paket untuk setiap platform.
xml "di server"
<?xml version='1.0' encoding='utf-8'?>
<Tools>
<CppCheck>
<windows executable="cppcheck.exe" />
<darwin executable="cppcheck" />
<linux executable="cppcheck" />
</CppCheck>
</Tools>
Dan pada proyek tersebut, tambahkan file dengan deskripsi tentang apa yang Anda butuhkan.
proyek xml
, , , . .
:
, β .
- , , ? -, . β , . : git.
-, β bash-, git: pull push, , git-.
, :
, , , .git/hooks. β . , ( Windows Mac), . , .
, . , .
. , , git-bash Windows. FAQ.
. , . , FAQ. , .git/hooks . , :
, , :
β . .git/hooks, . . , .git/hooks , .
, , - . , -. β . , β . :
, : , . , .
<spoiler title=Β« :> : 32- ; , 64-; pip install , . - 32- β .
<?xml version='1.0' encoding='utf-8'?>
<Tools>
<CppCheck version="1.89" />
</Tools>
, , , . .
python -m utility_runner --available-source D:\Playrix\![habr]\gd_hooks\available_source.xml --project-tools D:\Playrix\![habr]\gd_hooks\project\project_tools.xml --run-tool CppCheck -- --version
:
- , ,
- , , . .
, β .
- ?
- , , ? -, . β , . : git.
-, β bash-, git: pull push, , git-.
, :
- pre-commit β . , .
- prepare-commit-msg β , . , rebase.
- commit-msg β . , . , .
, , , .git/hooks. β . , ( Windows Mac), . , .
, . , .
. , , git-bash Windows. FAQ.
: , , dns . , curl
[ .
. , . , FAQ. , .git/hooks . , :
git rev-parse
git rev-parse --git-path hooks
, , :
|
|
| Worktree |
|
| submodule |
|
β . .git/hooks, . . , .git/hooks , .
, , - . , -. β . , β . :
- pre-commit , . pre-commit-tmp
- commit-msg pre-commit pre-commit-tmp
, : , . , .
<spoiler title=Β« :> : 32- ; , 64-; pip install , . - 32- β .
Tapi tetap, bagaimana cara meluncurkannya?
Pertama, kami membuat instruksi multi-halaman tentang croissant yang lebih enak , python mana yang akan diinstal. Tapi apakah kita ingat tentang desainer game dan telur orak-arik? Itu selalu terbakar: python dengan bitness yang salah, atau 2,7, bukan 3,7. Dan semua ini juga dikalikan dengan dua platform tempat pengguna bekerja: windows dan mac. (Pengguna Linux bersama kami ahli dan mengatur semuanya sendiri, diam-diam mengetuk suara rebana, atau mereka melewati masalah.)
Kami memecahkan masalah secara radikal - kami mengumpulkan python dari versi dan bit yang diperlukan. Dan untuk pertanyaan "bagaimana kami menaruhnya dan di mana menyimpannya" mereka menjawab: Nexus! Satu-satunya masalah: kami belum memiliki python untuk menjalankan skrip python yang kami buat untuk menjalankan utilitas dari Nexus.
Dan di situlah bash masuk! Dia tidak terlalu menakutkan, dan bahkan bagus jika Anda sudah terbiasa dengannya. Dan itu berfungsi di mana-mana: pada unix semuanya sudah baik-baik saja, dan pada Windows itu diinstal bersama dengan git-bash (ini adalah satu-satunya persyaratan kami untuk sistem lokal). Algoritma instalasi sangat sederhana:
- Unduh arsip python yang telah dikompilasi untuk platform yang diperlukan. Cara termudah untuk melakukannya adalah melalui curl - ini hampir ada di mana-mana (bahkan di Windows ).
Unduh pythonmkdir -p "$PYTHON_PRIMARY_DIR" curl "$PYTHON_NEXUS_URL" --output "$PYTHON_PRIMARY_DIR/ci_python.zip" --insecure || exit 1
- Buka zip, buat lingkungan virtual yang menautkan ke biner yang diunduh. Jangan ulangi kesalahan kami: jangan lupa untuk membuat versi virtualenv.
echo "Unzip python..." unzip "$PYTHON_PRIMARY_DIR/ci_python.zip" -d "$PYTHON_PRIMARY_DIR" > "unzip.log" rm -f "$PYTHON_PRIMARY_DIR/ci_python.zip" echo "Create virtual environment..." "$PYTHON_EXECUTABLE" -m pip install virtualenv==16.7.9 --disable-pip-version-check --no-warn-script-location
- Jika Anda memerlukan pustaka apa pun dari lib / *, Anda perlu menyalinnya sendiri. virtualenv tidak memikirkannya.
- Instal semua paket yang diperlukan. Di sini kami setuju dengan proyek bahwa mereka akan memiliki file ci / required.txt, yang akan berisi semua dependensi dalam format pip .
Menginstal dependensi
OUT_FILE="$VENV_DIR/pip_log.txt" "$PYTHON_VENV_EXECUTABLE" -m pip install -r "$REQUIRED_FILE" >> "$OUT_FILE" 2>&1 result=$? if [[ "$result" != "0" ]]; then var2=$(grep ERROR "$OUT_FILE") echo "$(tput setaf 3)" "$var2" "$(tput sgr 0)" echo -e "\e[1;31m" "Error while installing requirements. More details in: $OUT_FILE" "\e[0m" result=$ERR_PIP fi exit $result
Contoh Required.txt
pywin32==225;sys_platform == "win32" cryptography==3.0.0 google-api-python-client==1.7.11
Saat mereka mengatasi masalah, mereka biasanya melampirkan tangkapan layar dari konsol tempat kesalahan ditampilkan. Untuk mempermudah pekerjaan kami, kami tidak hanya menyimpan output dari proses pemasangan pip terakhir , tetapi juga menambahkan warna ke kehidupan, menampilkan kesalahan dalam warna dari log langsung ke konsol. Hidup grep!
Bagaimana tampilannya
Sekilas, sepertinya kita tidak membutuhkan lingkungan virtual. Bagaimanapun, kami telah mengunduh biner terpisah, ke direktori terpisah. Meskipun ada beberapa folder tempat sistem kami di-deploy, binernya masih berbeda. Tapi! Virtualenv memiliki skrip pengaktifan yang membuatnya sedemikian rupa sehingga python dapat dipanggil seolah-olah berada di lingkungan global. Ini mengisolasi eksekusi skrip dan membuatnya lebih mudah untuk diluncurkan.
Bayangkan: Anda perlu menjalankan file batch dari mana skrip python dijalankan, dari mana skrip python lain dijalankan. Ini bukan contoh fiksi - ini adalah cara kejadian pasca-pembangunan dijalankan saat membangun aplikasi. Tanpa virtualenv, Anda harus menghitung jalur yang diperlukan di mana saja dengan cepat, tetapi dengan mengaktifkankami hanya menggunakan python di mana-mana . Lebih tepatnya, vpython - kami telah menambahkan pembungkus kami sendiri untuk membuatnya lebih mudah dijalankan baik dari konsol maupun dari skrip. Di shell, kami memeriksa apakah kami sudah berada di lingkungan yang diaktifkan atau belum, apakah kami berjalan di TeamCity (di mana lingkungan virtual kami berada), dan pada saat yang sama kami menyiapkan lingkungan.
vpython.cmd
set CUR_DIR=%~dp0 set "REPO_DIR=%CUR_DIR%\." rem VIRTUAL_ENV is the variable from activate.bat and is set automatically rem TEAMCITY - if we are running from agent we need no virtualenv activation if "%VIRTUAL_ENV%"=="" IF "%TEAMCITY%"=="" ( set RETURN=if_state goto prepare :if_state if %ERRORLEVEL% neq 0 ( echo [31m Error while prepare environment. Run ci\PrepareAll.cmd via command line [0m exit /b 1 ) call "%REPO_DIR%\.venv\Scripts\activate.bat" rem special variable to check if venv activated from this script set VENV_FROM_CURRENT=true ) rem Run simple python and forward args to it python %* SET result=%errorlevel% if "%VENV_FROM_CURRENT%"=="true" ( call "%REPO_DIR%\.venv\Scripts\deactivate.bat" set CI_VENV_RUN= set VENV_FROM_CURRENT= ) :eof exit /b %result% :prepare setlocal set RUN_FROM_SCRIPT=true call "%REPO_DIR%\ci\PrepareEnvironment.cmd" > NUL endlocal goto %RETURN%
Tanakan, atau jangan lupa untuk menguji
Kami telah memecahkan masalah kelupaan untuk menjalankan pengujian, tetapi bahkan satu skrip pun dapat diabaikan. Karena itu, mereka membuat pil pelupa. Ini memiliki dua bagian.
Ketika sistem kami mulai, itu mengubah komentar komit dan menandainya sebagai "disetujui." Sebagai label, kami memutuskan untuk tidak memfilsafat dan menambahkan [+] atau [-] di akhir komentar pada komit.
Sebuah skrip sedang berjalan di server yang mem-parsing pesan, dan jika tidak menemukan kumpulan karakter yang didambakan, itu membuat tugas untuk pembuatnya. Ini adalah solusi yang paling sederhana dan elegan. Karakter yang tidak dapat dicetak tidak terlihat jelas. Untuk menjalankan kait server, Anda memerlukan paket tarif yang berbeda di GitHub, dan tidak ada yang akan membeli premium untuk satu fitur. Menelusuri sejarah komitmen, mencari simbol dan menetapkan tugas sudah jelas dan tidak terlalu mahal.
Ya, Anda dapat meletakkan simbol dengan pena Anda sendiri, tetapi apakah Anda yakin tidak akan merusak perakitan di server? Dan jika Anda memecahkannya ... ya, pria botak dari Homescapes itu sudah mengikuti Anda.
Apa intinya
Agak sulit untuk melacak jumlah kesalahan yang ditemukan oleh hook - mereka tidak sampai ke server. Hanya ada pendapat subjektif bahwa ada lebih banyak majelis hijau. Namun, ada juga sisi negatifnya - komitmen mulai memakan waktu cukup lama. Dalam beberapa kasus, ini bisa memakan waktu hingga 10 menit, tetapi itu adalah cerita terpisah tentang pengoptimalan.