diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml new file mode 100644 index 0000000..c8fcfab --- /dev/null +++ b/.github/workflows/pytest.yml @@ -0,0 +1,27 @@ +name: pytest + +on: + pull_request: + push: + +jobs: + pytest: + runs-on: windows-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Upgrade pip + run: python -m pip install --upgrade pip + + - name: Install dev dependencies + run: python -m pip install -r requirements-dev.txt + + - name: Run pytest + run: python -m pytest diff --git a/prompts/codex_task_template.md b/prompts/codex_task_template.md new file mode 100644 index 0000000..1e12da6 --- /dev/null +++ b/prompts/codex_task_template.md @@ -0,0 +1,31 @@ +# Codex作業依頼テンプレート + +## 目的 + +- + +## 変更してよいファイル + +- + +## 変更禁止ファイル + +- + +## 作業ルール + +- 実機通信は禁止。 +- `real_device_check.py` は実行しない。 +- `git commit`、`git push`、PR mergeは禁止。 +- pytest未実行を成功扱いしない。 +- 不明な通信仕様は推測で実装しない。 +- 通信仕様に関わる変更が必要な場合は、根拠となる資料と確認事項を先に提示する。 + +## 変更後の報告項目 + +1. 変更ファイル一覧 +2. 主な変更点 +3. 通信仕様に関わる変更の有無 +4. pytestを実行できたかどうか +5. 実機確認が必要な項目 +6. 次に行うべき作業 diff --git a/scripts/dev_check.ps1 b/scripts/dev_check.ps1 new file mode 100644 index 0000000..286fba1 --- /dev/null +++ b/scripts/dev_check.ps1 @@ -0,0 +1,79 @@ +Set-StrictMode -Version Latest +$ErrorActionPreference = "Stop" + +$repoRoot = Resolve-Path (Join-Path $PSScriptRoot "..") +$failed = $false + +function Invoke-Check { + param( + [Parameter(Mandatory = $true)] + [string]$Title, + + [Parameter(Mandatory = $true)] + [scriptblock]$Command + ) + + Write-Host "" + Write-Host "== $Title ==" + + try { + $global:LASTEXITCODE = 0 + & $Command + if ($LASTEXITCODE -ne 0) { + throw "Exit code $LASTEXITCODE" + } + } + catch { + Write-Host "ERROR: $Title failed: $_" -ForegroundColor Red + $script:failed = $true + } +} + +Push-Location $repoRoot +try { + Invoke-Check "git status --short" { + git status --short + } + + Invoke-Check "py -m pytest" { + py -m pytest + } + + $scanScript = Join-Path $PSScriptRoot ("sec" + "ret_scan.ps1") + Invoke-Check "blocked text scan" { + & $scanScript + } + + Write-Host "" + Write-Host "== .gitignore check ==" + $gitignore = Join-Path $repoRoot ".gitignore" + if (-not (Test-Path -LiteralPath $gitignore)) { + Write-Host "ERROR: .gitignore was not found." -ForegroundColor Red + $failed = $true + } + else { + $ignored = Get-Content -LiteralPath $gitignore | + ForEach-Object { $_.Trim() } | + Where-Object { $_ -eq "logs/real_device/" } + + if ($ignored) { + Write-Host "logs/real_device/ is ignored." + } + else { + Write-Host "ERROR: logs/real_device/ is not listed in .gitignore." -ForegroundColor Red + $failed = $true + } + } +} +finally { + Pop-Location +} + +if ($failed) { + Write-Host "ERROR: dev_check failed." -ForegroundColor Red + exit 1 +} + +Write-Host "" +Write-Host "dev_check passed." +exit 0 diff --git a/scripts/secret_scan.ps1 b/scripts/secret_scan.ps1 new file mode 100644 index 0000000..5f77f98 --- /dev/null +++ b/scripts/secret_scan.ps1 @@ -0,0 +1,80 @@ +Set-StrictMode -Version Latest +$ErrorActionPreference = "Stop" + +$repoRoot = Resolve-Path (Join-Path $PSScriptRoot "..") +$allowedExtensions = @( + ".md", + ".py", + ".ps1", + ".txt", + ".yml", + ".yaml", + ".json", + ".toml" +) +$allowedNames = @( + ".gitignore", + ".gitattributes" +) +$excludedDirs = @( + ".git", + "logs", + "__pycache__", + ".pytest_cache" +) +$terms = @( + ("10" + "." + "26" + "."), + ("Client" + "Socket"), + ("pass" + "word"), + ("sec" + "ret"), + ("to" + "ken"), + ("api" + "key"), + ("api" + "_key") +) +$pattern = ($terms | ForEach-Object { [regex]::Escape($_) }) -join "|" +$found = $false + +function Get-RepoRelativePath { + param( + [Parameter(Mandatory = $true)] + [string]$Path + ) + + $rootPath = $repoRoot.ProviderPath.TrimEnd("\", "/") + [System.IO.Path]::DirectorySeparatorChar + $rootUri = [System.Uri]::new($rootPath) + $pathUri = [System.Uri]::new($Path) + return [System.Uri]::UnescapeDataString($rootUri.MakeRelativeUri($pathUri).ToString()).Replace("/", [System.IO.Path]::DirectorySeparatorChar) +} + +Get-ChildItem -LiteralPath $repoRoot -Recurse -File -Force -ErrorAction SilentlyContinue | + Where-Object { + $file = $_ + $relative = Get-RepoRelativePath -Path $file.FullName + $parts = $relative -split "[\\/]+" + + foreach ($dir in $excludedDirs) { + if ($parts -contains $dir) { + return $false + } + } + + $extension = $file.Extension.ToLowerInvariant() + return ($allowedExtensions -contains $extension) -or ($allowedNames -contains $file.Name) + } | + ForEach-Object { + $file = $_ + $matches = Select-String -LiteralPath $file.FullName -Pattern $pattern + foreach ($match in $matches) { + $found = $true + $relative = Get-RepoRelativePath -Path $file.FullName + Write-Host ("{0}:{1}: {2}" -f $relative, $match.LineNumber, $match.Line.Trim()) + } + } + +if ($found) { + Write-Error "Blocked text was found." + exit 1 +} + +Write-Host "No blocked text found." +exit 0