diff --git a/.github/AGENTS.md b/.github/AGENTS.md index 41573dd93a..6008ff8933 100644 --- a/.github/AGENTS.md +++ b/.github/AGENTS.md @@ -27,6 +27,8 @@ Short repo-level instructions for agents. - Use GitHub issues/PRs for local issue flow. - For `LT-` tickets, use the Atlassian skill scripts; do not attempt direct Jira URL browsing. +- GitHub Copilot for Jira may be used with authenticated Jira Data Center access when the approved service-user/API policy limits which issues are exposed. +- Setup and policy notes: `.github/copilot-jira-setup.md` ## Relevant files diff --git a/.github/copilot-jira-setup.md b/.github/copilot-jira-setup.md new file mode 100644 index 0000000000..d1cfac94f6 --- /dev/null +++ b/.github/copilot-jira-setup.md @@ -0,0 +1,59 @@ +# GitHub Copilot For Jira Setup + +FieldWorks uses authenticated Jira Data Center access. Jira issues are not anonymously public, but some issues may be designated as safe for agent use through the approved service-user/API policy. + +The rule for this repository is: + +- agent access to Jira must go through the approved service user, +- the service user's API scope must determine which issues are exposed, +- personal Jira credentials must not be used for automated Copilot setup or workflow runs. + +## Repository Guidance + +The reusable workflow at `.github/workflows/copilot-setup-steps.yml` is intended to stay safe for Jira-triggered agent runs because it: + +- uses least-privilege repository permissions, +- avoids installer/signing/release secrets, +- allows Jira credentials only when they are supplied as part of the approved service-user setup, +- requires `JIRA_URL` when Jira API credentials are present. + +What this workflow does not enforce: + +- whether an individual Jira issue is approved for agent visibility, +- which Jira projects are in scope, +- the service user's effective permissions. + +Those controls belong in the Jira-side API/service-user policy. + +## Required Secrets + +For the FieldWorks Jira Data Center setup, the normal configuration is: + +- `JIRA_URL` +- `JIRA_PAT_TOKEN` + +If your integration layer uses different variable names, keep them mapped at the caller workflow or environment level and avoid changing this reusable workflow unless the contract changes. + +Guidance: + +- Prefer environment or organization secrets over ad hoc per-workflow values. +- Use a dedicated service-user PAT or API token. +- Do not use a personal admin token. +- Scope the service account so the API only exposes issues marked for agent/public handling. + +## Optional Variants + +Other helper tooling in this repository may still support: + +- Jira Cloud: `JIRA_URL`, `JIRA_USERNAME`, `JIRA_API_TOKEN` +- Jira Data Center: `JIRA_URL`, `JIRA_PAT_TOKEN` + +For FieldWorks, the Data Center service-user path is the intended default. + +## Recommended Admin Setup + +1. Create a dedicated Jira service user for Copilot/agent access. +2. Grant that service user only the minimum permissions needed to read issues approved for agent use. +3. Enforce issue visibility through your API layer or Jira-side policy, rather than assuming anonymous public access. +4. Store `JIRA_URL` and `JIRA_PAT_TOKEN` as managed secrets at the environment or organization level. +5. Keep direct browsing of Jira URLs out of agent tooling unless it is going through the approved authenticated integration path. \ No newline at end of file diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index 390f5f22c9..ff345f36ba 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -1,14 +1,51 @@ -# Reusable workflow to setup Windows build environment for AI coding agents -# Optimized for windows-latest which already includes most FieldWorks dependencies. +# Direct Copilot setup workflow for Windows-based GitHub Copilot coding agent runs +# Optimized for windows-2022 which already includes most FieldWorks dependencies. +# This workflow is for managed/native validation only. +# +# JIRA INTEGRATION POLICY: +# - This workflow may be used by GitHub Copilot coding agent sessions that were +# started from Jira. +# - FieldWorks uses authenticated Jira Data Center access, not anonymous public +# Jira access. +# - Jira content exposed to agent runs must be filtered through the approved +# service-user/API policy so only issues intended for agent use are surfaced. +# - If Jira credentials are provided to a caller workflow, they must be +# least-privilege service-user credentials, not personal high-privilege +# credentials. +# +# IMPORTANT: Do not run installer/signing/deployment builds here. +# Rationale: cloud agent jobs should not require high-privilege credentials +# (signing, release upload, deployment) and should minimize secret exposure. +# +# ACCEPTED RESIDUAL RISK: +# - GitHub's Windows Copilot guidance explicitly notes that the integrated +# Copilot firewall is not compatible with Windows runs. In addition, GitHub +# documents that the agent firewall does not protect processes started in +# copilot-setup-steps, even where the firewall is otherwise enabled. +# - As a result, this workflow is designed under the assumption that setup-step +# traffic could exfiltrate repository data or any credentials intentionally +# exposed to the agent environment. +# - We accept that residual risk for this repository because the credentials in +# scope here are deliberately low-privilege and narrowly scoped: `contents: read` +# for checkout only, Jira service-user credentials limited by Jira/API policy, +# and no installer/signing/deployment secrets. +# - Additional exposure that remains in scope: package/dependency downloads, +# shallow helper-repo clones, and any network access performed by build/test +# tooling during setup. +# - This acceptance is conditional on keeping this workflow limited to managed/ +# native validation and never adding high-privilege release, signing, deployment, +# or broadly scoped cross-system credentials. +# - If stronger exfiltration controls become required, move Copilot Windows runs +# to a self-hosted ephemeral runner or a larger runner with Azure private +# networking, and re-review this workflow before adding new secrets. # # ============================================================================ -# EXPECTED FROM windows-latest (DO NOT RE-INSTALL): +# EXPECTED FROM windows-2022 (DO NOT RE-INSTALL): # ============================================================================ # - Visual Studio 2022 Enterprise (with Desktop & C++ workloads) # - MSBuild (via VS 2022) # - .NET Framework 4.8.1 SDK & Targeting Pack # - Windows SDK (10.0.17763+, 19041, 22621, 26100) -# - WiX Toolset 3.14.x # - NuGet CLI # - .NET SDK 8.x and 9.x # - LLVM/clangd (for Serena C++ language server) @@ -34,106 +71,178 @@ # # ============================================================================ -name: "Agents: Windows setup steps" +name: "Copilot Setup Steps" + +# Copilot discovers and runs this workflow by filename/job name when it starts +# an agent session from the default branch. Keep manual dispatch for explicit +# validation, but do not subscribe this workflow to normal PR/push CI events. on: - workflow_call: - inputs: - msbuild_args: - required: false - type: string - description: 'Arguments to pass to msbuild (e.g., "FieldWorks.sln /t:RunTests /p:Configuration=Debug")' - skip_serena: - required: false - type: boolean - default: false - description: 'Skip Serena MCP setup (for builds that do not need code intelligence)' + workflow_dispatch: jobs: - windows-setup: - runs-on: windows-latest - outputs: - msbuild-path: ${{ steps.setup-env.outputs.msbuild-path }} - vs-install-path: ${{ steps.setup-env.outputs.vs-install-path }} - - steps: - - name: Checkout repo - uses: actions/checkout@v4 - - # ================================================================ - # CACHING - speeds up repeat runs significantly - # ================================================================ - - name: Cache NuGet packages - uses: actions/cache@v5 - with: - path: ~/.nuget/packages - key: nuget-${{ runner.os }}-${{ hashFiles('**/*.csproj', '**/packages.config', '**/Directory.Packages.props') }} - restore-keys: | - nuget-${{ runner.os }}- - - - name: Cache Serena language servers - if: inputs.skip_serena != true - uses: actions/cache@v5 - with: - path: | - ~/.cache/serena - ~/AppData/Local/serena - key: serena-lsp-${{ runner.os }}-v1 - restore-keys: | - serena-lsp-${{ runner.os }}- - - # ================================================================ - # ENVIRONMENT SETUP - # Scripts are in Build/Agent/ and can be run locally for testing - # ================================================================ - - name: Setup MSBuild PATH - uses: microsoft/setup-msbuild@v2 - - - name: Setup uv (Python package manager for Serena) - if: inputs.skip_serena != true - uses: astral-sh/setup-uv@v7 - with: - version: "latest" - - - name: Configure FieldWorks build environment - id: setup-env - shell: pwsh - run: | - # Run testable script with GitHub Actions output - .\Build\Agent\Setup-FwBuildEnv.ps1 -OutputGitHubEnv -Verify - - - name: Verify pre-installed dependencies - shell: pwsh - run: | - # Run testable script with strict checking - .\Build\Agent\Verify-FwDependencies.ps1 -FailOnMissing - - # ================================================================ - # SERENA MCP SETUP - for agent code intelligence - # ================================================================ - - name: "Serena: Setup and verify" - if: inputs.skip_serena != true - shell: pwsh - timeout-minutes: 10 - run: | - # Run testable script with GitHub Actions output - .\Build\Agent\Setup-Serena.ps1 -OutputGitHubEnv -SkipLanguageServerCheck - - # ================================================================ - # BUILD (optional) - # ================================================================ - - name: Initialize VS Developer Environment - if: inputs.msbuild_args != '' && inputs.msbuild_args != null - uses: ilammy/msvc-dev-cmd@v1 - with: - arch: x64 - - - name: Run msbuild - if: inputs.msbuild_args != '' && inputs.msbuild_args != null - shell: pwsh - run: | - Write-Host "Running: msbuild ${{ inputs.msbuild_args }}" -ForegroundColor Cyan - msbuild ${{ inputs.msbuild_args }} - if ($LASTEXITCODE -ne 0) { - throw "MSBuild failed with exit code $LASTEXITCODE" - } - Write-Host "Build completed successfully!" -ForegroundColor Green + copilot-setup-steps: + runs-on: windows-2022 + permissions: + contents: read + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + # ================================================================ + # CACHING - speeds up repeat runs significantly + # ================================================================ + - name: Cache NuGet packages + uses: actions/cache@v5 + with: + path: packages + key: nuget-${{ runner.os }}-${{ hashFiles('**/*.csproj', '**/packages.config', '**/Directory.Packages.props', 'nuget.config') }} + restore-keys: | + nuget-${{ runner.os }}- + + - name: Cache Serena language servers + uses: actions/cache@v5 + with: + path: | + ~/.cache/serena + ~/AppData/Local/serena + key: serena-lsp-${{ runner.os }}-${{ hashFiles('.serena/project.yml', 'Build/Agent/Setup-Serena.ps1', '.github/workflows/copilot-setup-steps.yml') }} + restore-keys: | + serena-lsp-${{ runner.os }}- + + # ================================================================ + # ENVIRONMENT SETUP + # Scripts are in Build/Agent/ and can be run locally for testing + # ================================================================ + - name: Setup MSBuild PATH + uses: microsoft/setup-msbuild@v2 + + - name: Setup uv (Python package manager for Serena) + uses: astral-sh/setup-uv@v7 + with: + version: "0.6.x" + + - name: Configure FieldWorks build environment + shell: pwsh + run: | + # Run testable script with GitHub Actions output + .\Build\Agent\Setup-FwBuildEnv.ps1 -OutputGitHubEnv -Verify + + - name: Verify pre-installed dependencies + shell: pwsh + run: | + # Run testable script with strict checking + .\Build\Agent\Verify-FwDependencies.ps1 -FailOnMissing + + - name: Guardrail - Jira integration policy + shell: pwsh + run: | + Write-Host "Jira policy: authenticated Jira Data Center access is allowed when scoped through the approved service-user/API policy." -ForegroundColor Cyan + + $jiraUrl = [Environment]::GetEnvironmentVariable('JIRA_URL') + if (-not [string]::IsNullOrWhiteSpace($jiraUrl) -and $jiraUrl -notmatch '^https://') { + throw "Unsupported JIRA_URL '$jiraUrl'. Jira URLs must use HTTPS." + } + + $jiraTokenVars = @( + 'JIRA_PAT_TOKEN', + 'JIRA_API_TOKEN', + 'ATLASSIAN_API_TOKEN' + ) + + $presentTokenVars = @( + $jiraTokenVars | + Where-Object { + $value = [Environment]::GetEnvironmentVariable($_) + -not [string]::IsNullOrWhiteSpace($value) + } + ) + + if ($presentTokenVars.Count -gt 0 -and [string]::IsNullOrWhiteSpace($jiraUrl)) { + throw "Jira credentials were provided without JIRA_URL. Set JIRA_URL alongside the service-user token configuration." + } + + if ($presentTokenVars.Count -gt 0) { + Write-Host "Jira service-user credentials detected. Ensure the token is scoped by the approved visibility API/policy." -ForegroundColor DarkGray + } + + - name: Clone helper repos (shallow) + shell: pwsh + run: | + function Invoke-ShallowClone { + param( + [Parameter(Mandatory = $true)][string]$RepoUrl, + [Parameter(Mandatory = $true)][string]$Destination, + [string]$Branch = "" + ) + + if (Test-Path (Join-Path $Destination '.git')) { + Write-Host "Already cloned: $Destination" -ForegroundColor DarkGray + return + } + + if (Test-Path $Destination) { + Remove-Item -Path $Destination -Recurse -Force + } + + $parent = Split-Path -Parent $Destination + if (-not (Test-Path $parent)) { + New-Item -ItemType Directory -Path $parent -Force | Out-Null + } + + if ([string]::IsNullOrWhiteSpace($Branch)) { + git clone --depth 1 $RepoUrl $Destination + } + else { + git clone --depth 1 --branch $Branch $RepoUrl $Destination + } + + if ($LASTEXITCODE -ne 0) { + throw "Failed to clone $RepoUrl into $Destination" + } + } + + Invoke-ShallowClone -RepoUrl 'https://github.com/sillsdev/FwHelps.git' -Destination 'DistFiles/Helps' + Invoke-ShallowClone -RepoUrl 'https://github.com/sillsdev/FwLocalizations.git' -Destination 'Localizations' + Invoke-ShallowClone -RepoUrl 'https://github.com/sillsdev/liblcm.git' -Destination 'Localizations/LCM' + + # ================================================================ + # SERENA MCP SETUP - for agent code intelligence + # ================================================================ + - name: "Serena: Setup and verify" + shell: pwsh + timeout-minutes: 10 + run: | + # Run testable script with GitHub Actions output + .\Build\Agent\Setup-Serena.ps1 -OutputGitHubEnv -SkipLanguageServerCheck + + # ================================================================ + # BUILD AND TEST VALIDATION + # This workflow always pre-seeds the Copilot environment the same way: + # Debug build, include managed test binaries, then run managed tests. + # ================================================================ + - name: Run build.ps1 + shell: pwsh + timeout-minutes: 60 + run: | + $buildArgs = @('-Configuration', 'Debug', '-BuildTests') + + Write-Host "Running: ./build.ps1 $($buildArgs -join ' ')" -ForegroundColor Cyan + ./build.ps1 @buildArgs + if ($LASTEXITCODE -ne 0) { + throw "build.ps1 failed with exit code $LASTEXITCODE" + } + Write-Host "build.ps1 completed successfully!" -ForegroundColor Green + + - name: Run test.ps1 + shell: pwsh + timeout-minutes: 60 + run: | + $testArgs = @('-Configuration', 'Debug', '-NoBuild') + + Write-Host "Running: ./test.ps1 $($testArgs -join ' ')" -ForegroundColor Cyan + ./test.ps1 @testArgs + if ($LASTEXITCODE -ne 0) { + throw "test.ps1 failed with exit code $LASTEXITCODE" + } + Write-Host "test.ps1 completed successfully!" -ForegroundColor Green diff --git a/.gitignore b/.gitignore index 0e9fe4ddf8..e1fc649d03 100644 --- a/.gitignore +++ b/.gitignore @@ -77,6 +77,9 @@ _user.mak.lnx *.VC.opendb *.invalid *.code-workspace +.env +.env.* +.github/skills/**/.env .dotest/ .depend diff --git a/AGENTS.md b/AGENTS.md index 47406c6aa7..11addfd799 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -30,6 +30,8 @@ Minimal, high-signal guidance for coding agents in this repository. - Use GitHub issues/PRs (and Jira when required) for issue workflow. - For `LT-` Jira tickets, use the Atlassian Python skill scripts under `.github/skills/atlassian-readonly-skills/scripts`. - Do not attempt direct web access to Jira pages from agent tooling. +- GitHub Copilot for Jira may be used with authenticated Jira Data Center access when the approved service-user/API policy limits which issues are exposed. +- See `.github/copilot-jira-setup.md` for setup and secret guidance. ## Validation checklist