signate-deploy 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- signate_deploy-0.1.0/.github/workflows/publish.yml +27 -0
- signate_deploy-0.1.0/.github/workflows/test.yml +27 -0
- signate_deploy-0.1.0/.gitignore +8 -0
- signate_deploy-0.1.0/LICENSE +21 -0
- signate_deploy-0.1.0/PKG-INFO +146 -0
- signate_deploy-0.1.0/README.md +119 -0
- signate_deploy-0.1.0/pyproject.toml +46 -0
- signate_deploy-0.1.0/src/signate_deploy/__init__.py +3 -0
- signate_deploy-0.1.0/src/signate_deploy/cli.py +25 -0
- signate_deploy-0.1.0/src/signate_deploy/commands/__init__.py +0 -0
- signate_deploy-0.1.0/src/signate_deploy/commands/download.py +45 -0
- signate_deploy-0.1.0/src/signate_deploy/commands/init.py +139 -0
- signate_deploy-0.1.0/src/signate_deploy/commands/init_repo.py +213 -0
- signate_deploy-0.1.0/src/signate_deploy/commands/submit.py +49 -0
- signate_deploy-0.1.0/tests/__init__.py +0 -0
- signate_deploy-0.1.0/tests/test_init.py +69 -0
- signate_deploy-0.1.0/tests/test_init_repo.py +53 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
publish:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
permissions:
|
|
11
|
+
id-token: write
|
|
12
|
+
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- uses: actions/setup-python@v5
|
|
17
|
+
with:
|
|
18
|
+
python-version: "3.12"
|
|
19
|
+
|
|
20
|
+
- name: Install build tools
|
|
21
|
+
run: pip install build
|
|
22
|
+
|
|
23
|
+
- name: Build package
|
|
24
|
+
run: python -m build
|
|
25
|
+
|
|
26
|
+
- name: Publish to PyPI
|
|
27
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
name: Test
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.9", "3.10", "3.11", "3.12"]
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- uses: actions/setup-python@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: ${{ matrix.python-version }}
|
|
22
|
+
|
|
23
|
+
- name: Install dependencies
|
|
24
|
+
run: pip install -e ".[dev]"
|
|
25
|
+
|
|
26
|
+
- name: Run tests
|
|
27
|
+
run: pytest tests/ -v
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 yasunorim
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: signate-deploy
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A CLI tool to automate SIGNATE competition workflows via GitHub Actions
|
|
5
|
+
Project-URL: Homepage, https://github.com/yasumorishima/signate-deploy
|
|
6
|
+
Project-URL: Repository, https://github.com/yasumorishima/signate-deploy
|
|
7
|
+
Author: yasunorim
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: competition,deploy,github-actions,machine-learning,signate
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
21
|
+
Requires-Python: >=3.9
|
|
22
|
+
Requires-Dist: click>=8.0
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
25
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# signate-deploy
|
|
29
|
+
|
|
30
|
+
A CLI tool to automate SIGNATE competition workflows via GitHub Actions.
|
|
31
|
+
|
|
32
|
+
`git push` → GitHub Actions → Download data → Train → Submit to SIGNATE
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
signate-deploy init-repo # Set up GitHub Actions workflows
|
|
36
|
+
signate-deploy init my-comp \
|
|
37
|
+
--task-key <task_key> \
|
|
38
|
+
--file-key train:<key> \
|
|
39
|
+
--file-key test:<key> # Create competition directory
|
|
40
|
+
signate-deploy submit my-comp \
|
|
41
|
+
--memo "Baseline v1" # Trigger train & submit
|
|
42
|
+
signate-deploy download my-comp # Trigger data download only
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install signate-deploy
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Quick Start
|
|
52
|
+
|
|
53
|
+
### 1. Set up GitHub Actions
|
|
54
|
+
|
|
55
|
+
In your GitHub repository root:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
signate-deploy init-repo
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Creates:
|
|
62
|
+
- `.github/workflows/signate-submit.yml` — full pipeline (download → train → submit)
|
|
63
|
+
- `.github/workflows/signate-download.yml` — data download only
|
|
64
|
+
|
|
65
|
+
### 2. Set up GitHub Secrets
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
# Generate SIGNATE token
|
|
69
|
+
signate token --email=your@email.com --password=your-password
|
|
70
|
+
|
|
71
|
+
# Set as GitHub Secret (Base64 encoded)
|
|
72
|
+
cat ~/.signate/signate.json | base64 | gh secret set SIGNATE_TOKEN_B64
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 3. Get task_key and file_keys
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
pip install signate
|
|
79
|
+
signate file-list --task_key <task_key>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
`task_key` is in the competition URL:
|
|
83
|
+
```
|
|
84
|
+
https://user.competition.signate.jp/.../detail/?...&task=THIS_IS_TASK_KEY
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### 4. Create competition directory
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
signate-deploy init my-comp \
|
|
91
|
+
--task-key abc123def456 \
|
|
92
|
+
--file-key train:5f0e1ebb35af4963 \
|
|
93
|
+
--file-key test:72f23ebe8f004fa0 \
|
|
94
|
+
--file-key sample_submit:ad3502af26b9
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Creates:
|
|
98
|
+
```
|
|
99
|
+
my-comp/
|
|
100
|
+
signate-config.json # task_key and file_keys
|
|
101
|
+
train.py # LightGBM 5-fold CV template
|
|
102
|
+
requirements.txt # pandas, numpy, scikit-learn, lightgbm
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### 5. Edit train.py and push
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
# Edit my-comp/train.py (set TARGET column name, add preprocessing, etc.)
|
|
109
|
+
git add my-comp/ && git commit -m "Add my-comp baseline" && git push
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### 6. Submit
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
signate-deploy submit my-comp --memo "Baseline v1"
|
|
116
|
+
# → gh workflow run signate-submit.yml is triggered
|
|
117
|
+
|
|
118
|
+
# Check progress
|
|
119
|
+
gh run list --limit 1
|
|
120
|
+
gh run view --log
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## signate-config.json
|
|
124
|
+
|
|
125
|
+
```json
|
|
126
|
+
{
|
|
127
|
+
"task_key": "your_task_key",
|
|
128
|
+
"file_keys": {
|
|
129
|
+
"train": "file_key_for_train_csv",
|
|
130
|
+
"test": "file_key_for_test_csv",
|
|
131
|
+
"sample_submit": "file_key_for_sample_submit_csv"
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Notes
|
|
137
|
+
|
|
138
|
+
- `⚠️ Never commit `data/` or `.signate/` — they are .gitignored by `init-repo`
|
|
139
|
+
- Requires [GitHub CLI (`gh`)](https://cli.github.com/) to be installed and authenticated
|
|
140
|
+
- Works on any OS (Windows/Mac/Linux)
|
|
141
|
+
|
|
142
|
+
## Links
|
|
143
|
+
|
|
144
|
+
- [PyPI](https://pypi.org/project/signate-deploy/)
|
|
145
|
+
- [Article (JP)](https://zenn.dev/shogaku/articles/signate-github-actions-cloud-ml)
|
|
146
|
+
- [Related: kaggle-notebook-deploy](https://github.com/yasumorishima/kaggle-notebook-deploy)
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# signate-deploy
|
|
2
|
+
|
|
3
|
+
A CLI tool to automate SIGNATE competition workflows via GitHub Actions.
|
|
4
|
+
|
|
5
|
+
`git push` → GitHub Actions → Download data → Train → Submit to SIGNATE
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
signate-deploy init-repo # Set up GitHub Actions workflows
|
|
9
|
+
signate-deploy init my-comp \
|
|
10
|
+
--task-key <task_key> \
|
|
11
|
+
--file-key train:<key> \
|
|
12
|
+
--file-key test:<key> # Create competition directory
|
|
13
|
+
signate-deploy submit my-comp \
|
|
14
|
+
--memo "Baseline v1" # Trigger train & submit
|
|
15
|
+
signate-deploy download my-comp # Trigger data download only
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install signate-deploy
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
### 1. Set up GitHub Actions
|
|
27
|
+
|
|
28
|
+
In your GitHub repository root:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
signate-deploy init-repo
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Creates:
|
|
35
|
+
- `.github/workflows/signate-submit.yml` — full pipeline (download → train → submit)
|
|
36
|
+
- `.github/workflows/signate-download.yml` — data download only
|
|
37
|
+
|
|
38
|
+
### 2. Set up GitHub Secrets
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# Generate SIGNATE token
|
|
42
|
+
signate token --email=your@email.com --password=your-password
|
|
43
|
+
|
|
44
|
+
# Set as GitHub Secret (Base64 encoded)
|
|
45
|
+
cat ~/.signate/signate.json | base64 | gh secret set SIGNATE_TOKEN_B64
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 3. Get task_key and file_keys
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pip install signate
|
|
52
|
+
signate file-list --task_key <task_key>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`task_key` is in the competition URL:
|
|
56
|
+
```
|
|
57
|
+
https://user.competition.signate.jp/.../detail/?...&task=THIS_IS_TASK_KEY
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### 4. Create competition directory
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
signate-deploy init my-comp \
|
|
64
|
+
--task-key abc123def456 \
|
|
65
|
+
--file-key train:5f0e1ebb35af4963 \
|
|
66
|
+
--file-key test:72f23ebe8f004fa0 \
|
|
67
|
+
--file-key sample_submit:ad3502af26b9
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Creates:
|
|
71
|
+
```
|
|
72
|
+
my-comp/
|
|
73
|
+
signate-config.json # task_key and file_keys
|
|
74
|
+
train.py # LightGBM 5-fold CV template
|
|
75
|
+
requirements.txt # pandas, numpy, scikit-learn, lightgbm
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### 5. Edit train.py and push
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# Edit my-comp/train.py (set TARGET column name, add preprocessing, etc.)
|
|
82
|
+
git add my-comp/ && git commit -m "Add my-comp baseline" && git push
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### 6. Submit
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
signate-deploy submit my-comp --memo "Baseline v1"
|
|
89
|
+
# → gh workflow run signate-submit.yml is triggered
|
|
90
|
+
|
|
91
|
+
# Check progress
|
|
92
|
+
gh run list --limit 1
|
|
93
|
+
gh run view --log
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## signate-config.json
|
|
97
|
+
|
|
98
|
+
```json
|
|
99
|
+
{
|
|
100
|
+
"task_key": "your_task_key",
|
|
101
|
+
"file_keys": {
|
|
102
|
+
"train": "file_key_for_train_csv",
|
|
103
|
+
"test": "file_key_for_test_csv",
|
|
104
|
+
"sample_submit": "file_key_for_sample_submit_csv"
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Notes
|
|
110
|
+
|
|
111
|
+
- `⚠️ Never commit `data/` or `.signate/` — they are .gitignored by `init-repo`
|
|
112
|
+
- Requires [GitHub CLI (`gh`)](https://cli.github.com/) to be installed and authenticated
|
|
113
|
+
- Works on any OS (Windows/Mac/Linux)
|
|
114
|
+
|
|
115
|
+
## Links
|
|
116
|
+
|
|
117
|
+
- [PyPI](https://pypi.org/project/signate-deploy/)
|
|
118
|
+
- [Article (JP)](https://zenn.dev/shogaku/articles/signate-github-actions-cloud-ml)
|
|
119
|
+
- [Related: kaggle-notebook-deploy](https://github.com/yasumorishima/kaggle-notebook-deploy)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "signate-deploy"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "A CLI tool to automate SIGNATE competition workflows via GitHub Actions"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.9"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "yasunorim" },
|
|
14
|
+
]
|
|
15
|
+
keywords = ["signate", "deploy", "github-actions", "machine-learning", "competition"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Intended Audience :: Science/Research",
|
|
20
|
+
"License :: OSI Approved :: MIT License",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: Python :: 3.9",
|
|
23
|
+
"Programming Language :: Python :: 3.10",
|
|
24
|
+
"Programming Language :: Python :: 3.11",
|
|
25
|
+
"Programming Language :: Python :: 3.12",
|
|
26
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
27
|
+
]
|
|
28
|
+
dependencies = [
|
|
29
|
+
"click>=8.0",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[project.optional-dependencies]
|
|
33
|
+
dev = [
|
|
34
|
+
"pytest>=7.0",
|
|
35
|
+
"pytest-cov>=4.0",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[project.scripts]
|
|
39
|
+
signate-deploy = "signate_deploy.cli:main"
|
|
40
|
+
|
|
41
|
+
[project.urls]
|
|
42
|
+
Homepage = "https://github.com/yasumorishima/signate-deploy"
|
|
43
|
+
Repository = "https://github.com/yasumorishima/signate-deploy"
|
|
44
|
+
|
|
45
|
+
[tool.hatch.build.targets.wheel]
|
|
46
|
+
packages = ["src/signate_deploy"]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""CLI entry point for signate-deploy."""
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
from signate_deploy import __version__
|
|
6
|
+
from signate_deploy.commands.init_repo import init_repo
|
|
7
|
+
from signate_deploy.commands.init import init
|
|
8
|
+
from signate_deploy.commands.submit import submit
|
|
9
|
+
from signate_deploy.commands.download import download
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@click.group()
|
|
13
|
+
@click.version_option(version=__version__)
|
|
14
|
+
def main():
|
|
15
|
+
"""GitHub Actions経由でSIGNATEコンペを自動化するCLIツール
|
|
16
|
+
|
|
17
|
+
データDL → 学習 → 提出 の一気通貫パイプラインをセットアップします。
|
|
18
|
+
"""
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
main.add_command(init_repo)
|
|
23
|
+
main.add_command(init)
|
|
24
|
+
main.add_command(submit)
|
|
25
|
+
main.add_command(download)
|
|
File without changes
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""signate-deploy download: GitHub Actions経由でデータをダウンロードする."""
|
|
2
|
+
|
|
3
|
+
import subprocess
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import click
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@click.command("download")
|
|
10
|
+
@click.argument("competition_dir")
|
|
11
|
+
def download(competition_dir):
|
|
12
|
+
"""GitHub Actions経由でSIGNATEからデータをダウンロードする.
|
|
13
|
+
|
|
14
|
+
COMPETITION_DIR 内の signate-config.json を使い、
|
|
15
|
+
GitHub Actions の signate-download.yml を起動します。
|
|
16
|
+
|
|
17
|
+
例:
|
|
18
|
+
signate-deploy download my-comp
|
|
19
|
+
"""
|
|
20
|
+
config_path = Path(competition_dir) / "signate-config.json"
|
|
21
|
+
if not config_path.exists():
|
|
22
|
+
click.echo(f"Error: {config_path} が見つかりません。", err=True)
|
|
23
|
+
click.echo("signate-deploy init でディレクトリを作成してください。", err=True)
|
|
24
|
+
raise SystemExit(1)
|
|
25
|
+
|
|
26
|
+
click.echo(f"Triggering download workflow for '{competition_dir}'...")
|
|
27
|
+
|
|
28
|
+
result = subprocess.run(
|
|
29
|
+
[
|
|
30
|
+
"gh", "workflow", "run", "signate-download.yml",
|
|
31
|
+
"-f", f"competition_dir={competition_dir}",
|
|
32
|
+
],
|
|
33
|
+
capture_output=True,
|
|
34
|
+
text=True,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
if result.returncode != 0:
|
|
38
|
+
click.echo("Error: gh workflow run に失敗しました。", err=True)
|
|
39
|
+
click.echo(result.stderr, err=True)
|
|
40
|
+
raise SystemExit(1)
|
|
41
|
+
|
|
42
|
+
click.echo("")
|
|
43
|
+
click.echo("Workflow を起動しました。")
|
|
44
|
+
click.echo("進捗確認: gh run list --limit 1")
|
|
45
|
+
click.echo("Artifact取得: gh run download <run_id> --dir data/")
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"""signate-deploy init: コンペ用ディレクトリを雛形から生成する."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import click
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
CONFIG_TEMPLATE = {
|
|
10
|
+
"task_key": "",
|
|
11
|
+
"file_keys": {},
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
TRAIN_TEMPLATE = """\
|
|
15
|
+
import pandas as pd
|
|
16
|
+
import numpy as np
|
|
17
|
+
from sklearn.model_selection import StratifiedKFold
|
|
18
|
+
from sklearn.metrics import roc_auc_score
|
|
19
|
+
import lightgbm as lgb
|
|
20
|
+
|
|
21
|
+
DATA_DIR = "{competition_dir}/data"
|
|
22
|
+
TARGET = "target" # ターゲット列名に変更してください
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def main():
|
|
26
|
+
train = pd.read_csv(f"{{DATA_DIR}}/train.csv")
|
|
27
|
+
test = pd.read_csv(f"{{DATA_DIR}}/test.csv")
|
|
28
|
+
|
|
29
|
+
features = [c for c in train.columns if c not in ["id", TARGET]]
|
|
30
|
+
X, y = train[features], train[TARGET]
|
|
31
|
+
X_test = test[features]
|
|
32
|
+
|
|
33
|
+
params = {{
|
|
34
|
+
"objective": "binary",
|
|
35
|
+
"metric": "auc",
|
|
36
|
+
"verbosity": -1,
|
|
37
|
+
"n_estimators": 1000,
|
|
38
|
+
"learning_rate": 0.05,
|
|
39
|
+
"random_state": 42,
|
|
40
|
+
}}
|
|
41
|
+
|
|
42
|
+
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
|
|
43
|
+
oof_preds = np.zeros(len(X))
|
|
44
|
+
test_preds = np.zeros(len(X_test))
|
|
45
|
+
|
|
46
|
+
for fold, (tr_idx, val_idx) in enumerate(skf.split(X, y)):
|
|
47
|
+
model = lgb.LGBMClassifier(**params)
|
|
48
|
+
model.fit(
|
|
49
|
+
X.iloc[tr_idx], y.iloc[tr_idx],
|
|
50
|
+
eval_set=[(X.iloc[val_idx], y.iloc[val_idx])],
|
|
51
|
+
callbacks=[lgb.early_stopping(50), lgb.log_evaluation(0)],
|
|
52
|
+
)
|
|
53
|
+
oof_preds[val_idx] = model.predict_proba(X.iloc[val_idx])[:, 1]
|
|
54
|
+
test_preds += model.predict_proba(X_test)[:, 1] / 5
|
|
55
|
+
print(f"Fold {{fold + 1}}: AUC = {{roc_auc_score(y.iloc[val_idx], oof_preds[val_idx]):.5f}}")
|
|
56
|
+
|
|
57
|
+
print(f"Overall OOF AUC: {{roc_auc_score(y, oof_preds):.5f}}")
|
|
58
|
+
|
|
59
|
+
submission = pd.DataFrame({{"id": test["id"], "pred": test_preds}})
|
|
60
|
+
submission.to_csv(f"{{DATA_DIR}}/../submission.csv", index=False, header=False)
|
|
61
|
+
print(f"Saved: submission.csv ({{len(submission)}} rows)")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
if __name__ == "__main__":
|
|
65
|
+
main()
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
REQUIREMENTS_TEMPLATE = """\
|
|
69
|
+
pandas
|
|
70
|
+
numpy
|
|
71
|
+
scikit-learn
|
|
72
|
+
lightgbm
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@click.command("init")
|
|
77
|
+
@click.argument("competition_dir")
|
|
78
|
+
@click.option("--task-key", required=True, help="SIGNATEのtask_key(コンペURLから取得)")
|
|
79
|
+
@click.option(
|
|
80
|
+
"--file-key",
|
|
81
|
+
multiple=True,
|
|
82
|
+
metavar="NAME:KEY",
|
|
83
|
+
help="ファイルキー(例: --file-key train:abc123 --file-key test:def456)",
|
|
84
|
+
)
|
|
85
|
+
def init(competition_dir, task_key, file_key):
|
|
86
|
+
"""コンペ用ディレクトリを雛形から生成する.
|
|
87
|
+
|
|
88
|
+
COMPETITION_DIR はローカルのディレクトリ名です。
|
|
89
|
+
|
|
90
|
+
例:
|
|
91
|
+
signate-deploy init my-comp --task-key abc123
|
|
92
|
+
signate-deploy init my-comp --task-key abc123 --file-key train:key1 --file-key test:key2
|
|
93
|
+
"""
|
|
94
|
+
dir_path = Path(competition_dir)
|
|
95
|
+
if dir_path.exists():
|
|
96
|
+
click.echo(f"Error: ディレクトリ '{competition_dir}' は既に存在します。", err=True)
|
|
97
|
+
raise SystemExit(1)
|
|
98
|
+
|
|
99
|
+
dir_path.mkdir(parents=True)
|
|
100
|
+
|
|
101
|
+
# file_keys をパース
|
|
102
|
+
file_keys = {}
|
|
103
|
+
for fk in file_key:
|
|
104
|
+
if ":" not in fk:
|
|
105
|
+
click.echo(f"Error: --file-key の形式が不正です(NAME:KEY 形式で指定してください): {fk}", err=True)
|
|
106
|
+
raise SystemExit(1)
|
|
107
|
+
name, key = fk.split(":", 1)
|
|
108
|
+
file_keys[name] = key
|
|
109
|
+
|
|
110
|
+
# signate-config.json
|
|
111
|
+
config = CONFIG_TEMPLATE.copy()
|
|
112
|
+
config["task_key"] = task_key
|
|
113
|
+
config["file_keys"] = file_keys
|
|
114
|
+
|
|
115
|
+
config_path = dir_path / "signate-config.json"
|
|
116
|
+
with open(config_path, "w") as f:
|
|
117
|
+
json.dump(config, f, indent=2, ensure_ascii=False)
|
|
118
|
+
f.write("\n")
|
|
119
|
+
click.echo(f" Created: {config_path}")
|
|
120
|
+
|
|
121
|
+
# train.py
|
|
122
|
+
train_path = dir_path / "train.py"
|
|
123
|
+
train_path.write_text(TRAIN_TEMPLATE.format(competition_dir=competition_dir))
|
|
124
|
+
click.echo(f" Created: {train_path}")
|
|
125
|
+
|
|
126
|
+
# requirements.txt
|
|
127
|
+
req_path = dir_path / "requirements.txt"
|
|
128
|
+
req_path.write_text(REQUIREMENTS_TEMPLATE)
|
|
129
|
+
click.echo(f" Created: {req_path}")
|
|
130
|
+
|
|
131
|
+
click.echo("")
|
|
132
|
+
click.echo(f"'{competition_dir}/' を作成しました。")
|
|
133
|
+
click.echo("")
|
|
134
|
+
click.echo("次のステップ:")
|
|
135
|
+
click.echo(f" 1. {train_path} を編集(TARGET列名等を変更)")
|
|
136
|
+
click.echo(f" 2. {config_path} のfile_keysを設定(まだなら)")
|
|
137
|
+
click.echo(f" 3. git add {competition_dir}/ && git commit && git push")
|
|
138
|
+
click.echo(f" 4. signate-deploy download {competition_dir} # データDL確認")
|
|
139
|
+
click.echo(f" 5. signate-deploy submit {competition_dir} --memo 'Baseline v1'")
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
"""signate-deploy init-repo: リポジトリにGitHub Actionsワークフローをセットアップする."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import click
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
SUBMIT_WORKFLOW = """\
|
|
9
|
+
name: SIGNATE Train & Submit
|
|
10
|
+
|
|
11
|
+
on:
|
|
12
|
+
workflow_dispatch:
|
|
13
|
+
inputs:
|
|
14
|
+
competition_dir:
|
|
15
|
+
description: "Competition directory name"
|
|
16
|
+
required: true
|
|
17
|
+
type: string
|
|
18
|
+
memo:
|
|
19
|
+
description: "Submission memo"
|
|
20
|
+
required: false
|
|
21
|
+
default: "GitHub Actions submission"
|
|
22
|
+
|
|
23
|
+
jobs:
|
|
24
|
+
submit:
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
steps:
|
|
27
|
+
- uses: actions/checkout@v4
|
|
28
|
+
|
|
29
|
+
- uses: actions/setup-python@v5
|
|
30
|
+
with:
|
|
31
|
+
python-version: "3.12"
|
|
32
|
+
|
|
33
|
+
- name: Setup SIGNATE token
|
|
34
|
+
run: |
|
|
35
|
+
mkdir -p ~/.signate
|
|
36
|
+
echo '${{ secrets.SIGNATE_TOKEN_B64 }}' | base64 -d > ~/.signate/signate.json
|
|
37
|
+
|
|
38
|
+
- name: Install dependencies
|
|
39
|
+
run: |
|
|
40
|
+
pip install signate
|
|
41
|
+
if [ -f "${{ inputs.competition_dir }}/requirements.txt" ]; then
|
|
42
|
+
pip install -r "${{ inputs.competition_dir }}/requirements.txt"
|
|
43
|
+
else
|
|
44
|
+
pip install pandas numpy scikit-learn lightgbm
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
- name: Download data
|
|
48
|
+
run: |
|
|
49
|
+
python - <<'EOF'
|
|
50
|
+
import json, subprocess, os
|
|
51
|
+
config = json.load(open("${{ inputs.competition_dir }}/signate-config.json"))
|
|
52
|
+
task_key = config["task_key"]
|
|
53
|
+
os.makedirs("${{ inputs.competition_dir }}/data", exist_ok=True)
|
|
54
|
+
os.chdir("${{ inputs.competition_dir }}/data")
|
|
55
|
+
for file_key in config.get("file_keys", {}).values():
|
|
56
|
+
subprocess.run(
|
|
57
|
+
["signate", "download", "--task_key", task_key, "--file_key", file_key],
|
|
58
|
+
check=True,
|
|
59
|
+
)
|
|
60
|
+
EOF
|
|
61
|
+
|
|
62
|
+
- name: Train and predict
|
|
63
|
+
run: python "${{ inputs.competition_dir }}/train.py"
|
|
64
|
+
|
|
65
|
+
- name: Submit
|
|
66
|
+
run: |
|
|
67
|
+
python - <<'EOF'
|
|
68
|
+
import json, subprocess
|
|
69
|
+
config = json.load(open("${{ inputs.competition_dir }}/signate-config.json"))
|
|
70
|
+
subprocess.run([
|
|
71
|
+
"signate", "submit",
|
|
72
|
+
"${{ inputs.competition_dir }}/submission.csv",
|
|
73
|
+
"--task_key", config["task_key"],
|
|
74
|
+
"--memo", "${{ inputs.memo }}",
|
|
75
|
+
], check=True)
|
|
76
|
+
EOF
|
|
77
|
+
|
|
78
|
+
- name: Upload submission as artifact
|
|
79
|
+
uses: actions/upload-artifact@v4
|
|
80
|
+
with:
|
|
81
|
+
name: submission-${{ github.run_number }}
|
|
82
|
+
path: ${{ inputs.competition_dir }}/submission.csv
|
|
83
|
+
retention-days: 90
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
DOWNLOAD_WORKFLOW = """\
|
|
87
|
+
name: SIGNATE Download Data
|
|
88
|
+
|
|
89
|
+
on:
|
|
90
|
+
workflow_dispatch:
|
|
91
|
+
inputs:
|
|
92
|
+
competition_dir:
|
|
93
|
+
description: "Competition directory name"
|
|
94
|
+
required: true
|
|
95
|
+
type: string
|
|
96
|
+
|
|
97
|
+
jobs:
|
|
98
|
+
download:
|
|
99
|
+
runs-on: ubuntu-latest
|
|
100
|
+
steps:
|
|
101
|
+
- uses: actions/checkout@v4
|
|
102
|
+
|
|
103
|
+
- uses: actions/setup-python@v5
|
|
104
|
+
with:
|
|
105
|
+
python-version: "3.12"
|
|
106
|
+
|
|
107
|
+
- name: Setup SIGNATE token
|
|
108
|
+
run: |
|
|
109
|
+
mkdir -p ~/.signate
|
|
110
|
+
echo '${{ secrets.SIGNATE_TOKEN_B64 }}' | base64 -d > ~/.signate/signate.json
|
|
111
|
+
|
|
112
|
+
- name: Install signate
|
|
113
|
+
run: pip install signate
|
|
114
|
+
|
|
115
|
+
- name: Download data
|
|
116
|
+
run: |
|
|
117
|
+
python - <<'EOF'
|
|
118
|
+
import json, subprocess, os
|
|
119
|
+
config = json.load(open("${{ inputs.competition_dir }}/signate-config.json"))
|
|
120
|
+
task_key = config["task_key"]
|
|
121
|
+
os.makedirs("${{ inputs.competition_dir }}/data", exist_ok=True)
|
|
122
|
+
os.chdir("${{ inputs.competition_dir }}/data")
|
|
123
|
+
for name, file_key in config.get("file_keys", {}).items():
|
|
124
|
+
print(f"Downloading {name}...")
|
|
125
|
+
subprocess.run(
|
|
126
|
+
["signate", "download", "--task_key", task_key, "--file_key", file_key],
|
|
127
|
+
check=True,
|
|
128
|
+
)
|
|
129
|
+
EOF
|
|
130
|
+
|
|
131
|
+
- name: Upload data as artifact
|
|
132
|
+
uses: actions/upload-artifact@v4
|
|
133
|
+
with:
|
|
134
|
+
name: signate-data-${{ inputs.competition_dir }}
|
|
135
|
+
path: ${{ inputs.competition_dir }}/data/
|
|
136
|
+
retention-days: 90
|
|
137
|
+
"""
|
|
138
|
+
|
|
139
|
+
GITIGNORE_ADDITIONS = """\
|
|
140
|
+
# === signate-deploy ===
|
|
141
|
+
# Data files
|
|
142
|
+
data/
|
|
143
|
+
*.csv
|
|
144
|
+
*.zip
|
|
145
|
+
|
|
146
|
+
# Credentials (NEVER commit these)
|
|
147
|
+
.signate/
|
|
148
|
+
signate.json
|
|
149
|
+
|
|
150
|
+
# Virtual environments
|
|
151
|
+
.venv/
|
|
152
|
+
venv/
|
|
153
|
+
"""
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
@click.command("init-repo")
|
|
157
|
+
@click.option("--force", "-f", is_flag=True, default=False, help="既存ファイルを上書きする")
|
|
158
|
+
def init_repo(force):
|
|
159
|
+
"""リポジトリにGitHub Actionsワークフローと.gitignoreをセットアップする.
|
|
160
|
+
|
|
161
|
+
カレントディレクトリに以下を生成します:
|
|
162
|
+
- .github/workflows/signate-submit.yml
|
|
163
|
+
- .github/workflows/signate-download.yml
|
|
164
|
+
- .gitignore への追記
|
|
165
|
+
"""
|
|
166
|
+
created = []
|
|
167
|
+
|
|
168
|
+
workflow_dir = Path(".github/workflows")
|
|
169
|
+
workflow_dir.mkdir(parents=True, exist_ok=True)
|
|
170
|
+
|
|
171
|
+
for filename, content in [
|
|
172
|
+
("signate-submit.yml", SUBMIT_WORKFLOW),
|
|
173
|
+
("signate-download.yml", DOWNLOAD_WORKFLOW),
|
|
174
|
+
]:
|
|
175
|
+
path = workflow_dir / filename
|
|
176
|
+
if path.exists() and not force:
|
|
177
|
+
click.echo(f" Skip: {path} (既に存在。--force で上書き)")
|
|
178
|
+
else:
|
|
179
|
+
path.write_text(content)
|
|
180
|
+
created.append(str(path))
|
|
181
|
+
click.echo(f" Created: {path}")
|
|
182
|
+
|
|
183
|
+
# .gitignore
|
|
184
|
+
gitignore_path = Path(".gitignore")
|
|
185
|
+
marker = "# === signate-deploy ==="
|
|
186
|
+
if gitignore_path.exists():
|
|
187
|
+
existing = gitignore_path.read_text()
|
|
188
|
+
if marker in existing and not force:
|
|
189
|
+
click.echo(f" Skip: {gitignore_path} (signate-deployセクション追加済み)")
|
|
190
|
+
else:
|
|
191
|
+
if marker not in existing:
|
|
192
|
+
with open(gitignore_path, "a") as f:
|
|
193
|
+
f.write("\n" + GITIGNORE_ADDITIONS)
|
|
194
|
+
created.append(f"{gitignore_path} (追記)")
|
|
195
|
+
click.echo(f" Updated: {gitignore_path}")
|
|
196
|
+
else:
|
|
197
|
+
gitignore_path.write_text(GITIGNORE_ADDITIONS)
|
|
198
|
+
created.append(str(gitignore_path))
|
|
199
|
+
click.echo(f" Created: {gitignore_path}")
|
|
200
|
+
|
|
201
|
+
click.echo("")
|
|
202
|
+
if created:
|
|
203
|
+
click.echo(f"{len(created)}個のファイルをセットアップしました。")
|
|
204
|
+
else:
|
|
205
|
+
click.echo("全てのファイルが既に存在しています。")
|
|
206
|
+
|
|
207
|
+
click.echo("")
|
|
208
|
+
click.echo("次のステップ:")
|
|
209
|
+
click.echo(" 1. GitHub Secretsを設定:")
|
|
210
|
+
click.echo(" signate token --email=your@email.com --password=your-password")
|
|
211
|
+
click.echo(" cat ~/.signate/signate.json | base64 | gh secret set SIGNATE_TOKEN_B64")
|
|
212
|
+
click.echo(" 2. コンペ用ディレクトリを作成:")
|
|
213
|
+
click.echo(" signate-deploy init <competition-dir> --task-key <task_key>")
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""signate-deploy submit: GitHub Actions経由でSIGNATEに提出する."""
|
|
2
|
+
|
|
3
|
+
import subprocess
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import click
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@click.command("submit")
|
|
10
|
+
@click.argument("competition_dir")
|
|
11
|
+
@click.option("--memo", "-m", default="GitHub Actions submission", help="提出メモ")
|
|
12
|
+
def submit(competition_dir, memo):
|
|
13
|
+
"""GitHub Actions経由でSIGNATEに提出する.
|
|
14
|
+
|
|
15
|
+
COMPETITION_DIR 内の signate-config.json を使い、
|
|
16
|
+
GitHub Actions の signate-submit.yml を起動します。
|
|
17
|
+
|
|
18
|
+
例:
|
|
19
|
+
signate-deploy submit my-comp
|
|
20
|
+
signate-deploy submit my-comp --memo "LightGBM baseline v1"
|
|
21
|
+
"""
|
|
22
|
+
config_path = Path(competition_dir) / "signate-config.json"
|
|
23
|
+
if not config_path.exists():
|
|
24
|
+
click.echo(f"Error: {config_path} が見つかりません。", err=True)
|
|
25
|
+
click.echo("signate-deploy init でディレクトリを作成してください。", err=True)
|
|
26
|
+
raise SystemExit(1)
|
|
27
|
+
|
|
28
|
+
click.echo(f"Triggering submit workflow for '{competition_dir}'...")
|
|
29
|
+
click.echo(f" Memo: {memo}")
|
|
30
|
+
|
|
31
|
+
result = subprocess.run(
|
|
32
|
+
[
|
|
33
|
+
"gh", "workflow", "run", "signate-submit.yml",
|
|
34
|
+
"-f", f"competition_dir={competition_dir}",
|
|
35
|
+
"-f", f"memo={memo}",
|
|
36
|
+
],
|
|
37
|
+
capture_output=True,
|
|
38
|
+
text=True,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
if result.returncode != 0:
|
|
42
|
+
click.echo("Error: gh workflow run に失敗しました。", err=True)
|
|
43
|
+
click.echo(result.stderr, err=True)
|
|
44
|
+
raise SystemExit(1)
|
|
45
|
+
|
|
46
|
+
click.echo("")
|
|
47
|
+
click.echo("Workflow を起動しました。")
|
|
48
|
+
click.echo("進捗確認: gh run list --limit 1")
|
|
49
|
+
click.echo("ログ確認: gh run view --log")
|
|
File without changes
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Tests for init command."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
from click.testing import CliRunner
|
|
7
|
+
from signate_deploy.cli import main
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_init_creates_directory(tmp_path, monkeypatch):
|
|
11
|
+
monkeypatch.chdir(tmp_path)
|
|
12
|
+
runner = CliRunner()
|
|
13
|
+
result = runner.invoke(main, ["init", "my-comp", "--task-key", "abc123"])
|
|
14
|
+
assert result.exit_code == 0
|
|
15
|
+
assert (tmp_path / "my-comp").is_dir()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_init_creates_config(tmp_path, monkeypatch):
|
|
19
|
+
monkeypatch.chdir(tmp_path)
|
|
20
|
+
runner = CliRunner()
|
|
21
|
+
result = runner.invoke(main, [
|
|
22
|
+
"init", "my-comp",
|
|
23
|
+
"--task-key", "abc123",
|
|
24
|
+
"--file-key", "train:key1",
|
|
25
|
+
"--file-key", "test:key2",
|
|
26
|
+
])
|
|
27
|
+
assert result.exit_code == 0
|
|
28
|
+
config = json.loads((tmp_path / "my-comp" / "signate-config.json").read_text())
|
|
29
|
+
assert config["task_key"] == "abc123"
|
|
30
|
+
assert config["file_keys"]["train"] == "key1"
|
|
31
|
+
assert config["file_keys"]["test"] == "key2"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_init_creates_train_py(tmp_path, monkeypatch):
|
|
35
|
+
monkeypatch.chdir(tmp_path)
|
|
36
|
+
runner = CliRunner()
|
|
37
|
+
result = runner.invoke(main, ["init", "my-comp", "--task-key", "abc123"])
|
|
38
|
+
assert result.exit_code == 0
|
|
39
|
+
assert (tmp_path / "my-comp" / "train.py").exists()
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_init_creates_requirements(tmp_path, monkeypatch):
|
|
43
|
+
monkeypatch.chdir(tmp_path)
|
|
44
|
+
runner = CliRunner()
|
|
45
|
+
result = runner.invoke(main, ["init", "my-comp", "--task-key", "abc123"])
|
|
46
|
+
assert result.exit_code == 0
|
|
47
|
+
assert (tmp_path / "my-comp" / "requirements.txt").exists()
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_init_fails_if_dir_exists(tmp_path, monkeypatch):
|
|
51
|
+
monkeypatch.chdir(tmp_path)
|
|
52
|
+
(tmp_path / "my-comp").mkdir()
|
|
53
|
+
runner = CliRunner()
|
|
54
|
+
result = runner.invoke(main, ["init", "my-comp", "--task-key", "abc123"])
|
|
55
|
+
assert result.exit_code != 0
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def test_init_invalid_file_key_format(tmp_path, monkeypatch):
|
|
59
|
+
monkeypatch.chdir(tmp_path)
|
|
60
|
+
runner = CliRunner()
|
|
61
|
+
result = runner.invoke(main, ["init", "my-comp", "--task-key", "abc123", "--file-key", "invalid"])
|
|
62
|
+
assert result.exit_code != 0
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def test_init_requires_task_key(tmp_path, monkeypatch):
|
|
66
|
+
monkeypatch.chdir(tmp_path)
|
|
67
|
+
runner = CliRunner()
|
|
68
|
+
result = runner.invoke(main, ["init", "my-comp"])
|
|
69
|
+
assert result.exit_code != 0
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Tests for init-repo command."""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from click.testing import CliRunner
|
|
5
|
+
from signate_deploy.cli import main
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_init_repo_creates_workflows(tmp_path, monkeypatch):
|
|
9
|
+
monkeypatch.chdir(tmp_path)
|
|
10
|
+
runner = CliRunner()
|
|
11
|
+
result = runner.invoke(main, ["init-repo"])
|
|
12
|
+
assert result.exit_code == 0
|
|
13
|
+
assert (tmp_path / ".github" / "workflows" / "signate-submit.yml").exists()
|
|
14
|
+
assert (tmp_path / ".github" / "workflows" / "signate-download.yml").exists()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_init_repo_creates_gitignore(tmp_path, monkeypatch):
|
|
18
|
+
monkeypatch.chdir(tmp_path)
|
|
19
|
+
runner = CliRunner()
|
|
20
|
+
result = runner.invoke(main, ["init-repo"])
|
|
21
|
+
assert result.exit_code == 0
|
|
22
|
+
assert (tmp_path / ".gitignore").exists()
|
|
23
|
+
content = (tmp_path / ".gitignore").read_text()
|
|
24
|
+
assert "signate-deploy" in content
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_init_repo_appends_to_existing_gitignore(tmp_path, monkeypatch):
|
|
28
|
+
monkeypatch.chdir(tmp_path)
|
|
29
|
+
(tmp_path / ".gitignore").write_text("*.pyc\n")
|
|
30
|
+
runner = CliRunner()
|
|
31
|
+
result = runner.invoke(main, ["init-repo"])
|
|
32
|
+
assert result.exit_code == 0
|
|
33
|
+
content = (tmp_path / ".gitignore").read_text()
|
|
34
|
+
assert "*.pyc" in content
|
|
35
|
+
assert "signate-deploy" in content
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_init_repo_skip_existing(tmp_path, monkeypatch):
|
|
39
|
+
monkeypatch.chdir(tmp_path)
|
|
40
|
+
runner = CliRunner()
|
|
41
|
+
runner.invoke(main, ["init-repo"])
|
|
42
|
+
result = runner.invoke(main, ["init-repo"])
|
|
43
|
+
assert result.exit_code == 0
|
|
44
|
+
assert "Skip" in result.output
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_init_repo_force_overwrite(tmp_path, monkeypatch):
|
|
48
|
+
monkeypatch.chdir(tmp_path)
|
|
49
|
+
runner = CliRunner()
|
|
50
|
+
runner.invoke(main, ["init-repo"])
|
|
51
|
+
result = runner.invoke(main, ["init-repo", "--force"])
|
|
52
|
+
assert result.exit_code == 0
|
|
53
|
+
assert "Skip" not in result.output
|