Skip to content

Feat: devcontainer pytest integration#110

Open
junkataoka wants to merge 2 commits intomainfrom
feature/devcontainer-pytest-integration
Open

Feat: devcontainer pytest integration#110
junkataoka wants to merge 2 commits intomainfrom
feature/devcontainer-pytest-integration

Conversation

@junkataoka
Copy link

@junkataoka junkataoka commented Dec 16, 2025

This pull request introduces a major refactor and modernization of the CI pipeline infrastructure for both Azure DevOps and GitHub Actions. The main improvements are the switch to modular, template-driven pipeline definitions, enhanced support for devcontainer-based testing, and improved code coverage reporting. The changes streamline CI setup, make it easier to add new projects, and improve compatibility across platforms (Linux and macOS). Documentation and configuration files have also been updated for clarity and consistency.

Azure DevOps Pipeline Modernization

  • Both .azuredevops/ado-ci-pipeline-ms-hosted.yml and .azuredevops/ado-ci-pipeline-self-hosted.yml are refactored to use a two-stage pipeline (Lint and Test), with jobs and steps defined via new templates for devcontainer testing and coverage publishing. This replaces legacy scripts and direct task definitions, making the pipeline more maintainable and extensible. [1] [2] [3]

Template-Driven CI Architecture

  • New reusable templates are introduced under .azuredevops/templates/, including test-devcontainer-job.yml for per-project devcontainer testing, run-devcontainer.yml for platform-aware container execution, setup-devcontainer.yml for environment setup, publish-test-results.yml for publishing test results and coverage, and merge-coverage.yml for merging and publishing code coverage reports. [1] [2] [3] [4] [5]

GitHub Actions Pipeline Improvements

  • .github/workflows/ci.yaml is reworked to use matrix jobs for devcontainer-based testing, artifact upload for results and coverage, and a dedicated lint job. A new job aggregates and publishes combined test results and coverage reports, improving visibility and maintainability. [1] [2]

Documentation and Configuration Updates

  • References to legacy scripts (e.g., ci-tests.sh) are removed from README.md and .env.example is updated for clarity, reflecting the new devcontainer and pipeline workflow. [1] [2] [3]

Platform Compatibility and Usability Enhancements

  • The new pipeline templates include logic for handling both Linux and macOS (Apple Silicon) agents, and ensure clean workspace setup and artifact handling for reliable CI runs. [1] [2]

Does this introduce a breaking change?

  • Yes
  • No

Author pre-publish checklist

  • No PII in logs or output
  • Made corresponding changes to the documentation
  • All new packages used are included in requirements.txt
  • Functions use type hints, and there are no type hint errors

Pull Request Type

What kind of change does this Pull Request introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Documentation content changes
  • Experiment notebook

@junkataoka
Copy link
Author

@junkataoka please read the following Contributor License Agreement(CLA). If you agree with the CLA, please reply with the following information.

@microsoft-github-policy-service agree [company="{your company}"]

Options:

  • (default - no company specified) I have sole ownership of intellectual property rights to my Submissions and I am not making Submissions in the course of work for my employer.
@microsoft-github-policy-service agree
  • (when company given) I am making Submissions in the course of work for my employer (or my employer has intellectual property rights in my Submissions by contract or applicable law). I have permission from my employer to make Submissions and enter into this Agreement on behalf of my employer. By signing below, the defined term “You” includes me and my employer.
@microsoft-github-policy-service agree company="Microsoft"

Contributor License Agreement

@microsoft-github-policy-service agree [company="microsoft"]

@junkataoka junkataoka force-pushed the feature/devcontainer-pytest-integration branch from 44faaca to ec09cba Compare December 16, 2025 08:43
@junkataoka
Copy link
Author

@microsoft-github-policy-service agree
@microsoft-github-policy-service agree company="Microsoft"

@junkataoka junkataoka force-pushed the feature/devcontainer-pytest-integration branch 5 times, most recently from 2523a23 to 0ebed45 Compare December 18, 2025 15:06
@junkataoka junkataoka changed the title Feature/devcontainer pytest integration feat: devcontainer pytest integration Dec 18, 2025
@junkataoka junkataoka changed the title feat: devcontainer pytest integration Feat: devcontainer pytest integration Dec 18, 2025
@junkataoka junkataoka force-pushed the feature/devcontainer-pytest-integration branch from d73bb8e to 82fb7b9 Compare December 19, 2025 00:10
@junkataoka junkataoka requested a review from Copilot December 19, 2025 00:20

This comment was marked as outdated.

This comment was marked as outdated.

@junkataoka junkataoka force-pushed the feature/devcontainer-pytest-integration branch 3 times, most recently from f22f8ef to d94a2d6 Compare December 19, 2025 02:55
- Add GitHub Actions workflow using official devcontainers/ci@v0.3 action
- Add Azure DevOps pipeline with custom templates for macOS compatibility
- Configure pytest with coverage reporting and JUnit XML output
- Handle workspace permission issues using /tmp with sudo
- Support CPU, GPU, and notebooks project configurations
- Add coverage merging and test result publishing for both platforms
- Skip pre-commit hook installation in CI when .git not available
- Add sync-to-azdo workflow for pipeline synchronization
- Update documentation with CI/CD setup instructions

GitHub Actions uses the official action which works well on Linux.
Azure DevOps uses custom templates to handle macOS-specific Docker issues
(path translation and working directory mount namespace errors).
@junkataoka junkataoka force-pushed the feature/devcontainer-pytest-integration branch from 672b9c6 to 164e6b9 Compare December 19, 2025 04:51
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 16 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@junkataoka junkataoka marked this pull request as ready for review December 19, 2025 05:21
@junkataoka junkataoka requested a review from bhavikm December 19, 2025 05:21
@junkataoka junkataoka requested a review from fujikosu December 19, 2025 05:21
@junkataoka junkataoka force-pushed the feature/devcontainer-pytest-integration branch from 0e665e8 to 5f6316e Compare December 19, 2025 09:25
Comment on lines +167 to +175

# Speckit / .specify related files
.specify/
specs/
*.speckit

# .github agents and prompts
.github/agents/
.github/prompts/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should these be out of .gitignore? we often manage .github/agents/ .github/prompts/ as a team and share prompts

Comment on lines +1 to +2
# For users who need AML, create .env in your project folder (e.g., src/sample_pytorch_gpu_project/.env)
# Optional: Set these variables if using the AML CLI v2 example under src/sample_pytorch_gpu_project
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the reason of deletion?

Comment on lines +19 to +20
"containerEnv": {
"PYTHONPATH": "/workspaces/repo"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a wrong path? Also as long as .env is there, it should be picked up automatically by vscode so this one is not needed

"postCreateCommand": "pre-commit install --overwrite",
"workspaceMount": "source=${localWorkspaceFolder},target=/workspaces/repo,type=bind",
"workspaceFolder": "/workspaces/repo",
"remoteUser": "devuser",
Copy link
Member

@fujikosu fujikosu Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

USER $USERNAME in Dockerfile side applies the same effect so this would be redundant

},
"workspaceMount": "source=${localWorkspaceFolder},target=/workspaces/repo,type=bind",
"workspaceFolder": "/workspaces/repo",
"remoteUser": "devuser",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be automatically set by "ghcr.io/devcontainers/features/common-utils:2" above?

Comment on lines +75 to +78
}
}
}
},
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this your vscode's setting? it'd be good to keep diff minimal as this PR is quite large and format change may not be worth including?

Comment on lines 78 to +81
}
},
}
}
},
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this your vscode's setting? it'd be good to keep diff minimal as this PR is quite large and format change may not be worth including?

"workspaceMount": "source=${localWorkspaceFolder},target=/workspaces/repo,type=bind",
"workspaceFolder": "/workspaces/repo",
"remoteUser": "devuser",
"postCreateCommand": "cd /workspaces/repo && pre-commit install --overwrite || echo 'pre-commit installation failed; continuing without pre-commit hooks'",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there any benefit of still going ahead when pre-commit fails? I'd rather it fail there as something would be wrong in that case

"workspaceMount": "source=${localWorkspaceFolder},target=/workspaces/repo,type=bind",
"workspaceFolder": "/workspaces/repo",
"remoteUser": "devuser",
"postCreateCommand": "cd /workspaces/repo && pre-commit install --overwrite || echo 'pre-commit installation failed; continuing without pre-commit hooks'",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

"postCreateCommand": "pre-commit install --overwrite",
"workspaceMount": "source=${localWorkspaceFolder},target=/workspaces/repo,type=bind",
"workspaceFolder": "/workspaces/repo",
"remoteUser": "devuser",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

USER $USERNAME in Dockerfile side applies the same effect so this would be redundant

],
"postCreateCommand": "pre-commit install --overwrite",
"workspaceMount": "source=${localWorkspaceFolder},target=/workspaces/repo,type=bind",
"workspaceFolder": "/workspaces/repo",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any strong reason we want to introduce this path and realted settings? Otherwise, I'd prefer to remove it to keep devcontainer.json bare minimum

"workspaceMount": "source=${localWorkspaceFolder},target=/workspaces/repo,type=bind",
"workspaceFolder": "/workspaces/repo",
"remoteUser": "devuser",
"postCreateCommand": "cd /workspaces/repo && pre-commit install --overwrite || echo 'pre-commit installation failed; continuing without pre-commit hooks'",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as others

],
"postCreateCommand": "pre-commit install --overwrite",
"workspaceMount": "source=${localWorkspaceFolder},target=/workspaces/repo,type=bind",
"workspaceFolder": "/workspaces/repo",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any strong reason we want to introduce this path? Otherwise, I'd prefer to remove it to keep devcontainer.json bare minimum

"${localWorkspaceFolder}/.env"
],
"postCreateCommand": "pre-commit install --overwrite",
"workspaceMount": "source=${localWorkspaceFolder},target=/workspaces/repo,type=bind",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we don't introduce "/workspaces/repo, I think this mount automatically runs without us specifying anything

"username": "devuser"
}
},
"initializeCommand": "test -f ${localWorkspaceFolder}/.env || touch ${localWorkspaceFolder}/.env",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how about creating .env from .env.example instead?

"dockerfile": "Dockerfile"
},
"shutdownAction": "none",
"updateRemoteUserUID": false,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

default setting =true allows devcontainer to match local side UID and avoids file permission issues from happening (ex: files created within container owned by different UID and local can't access it). Is there any benefit with this? I see file access issue being avoided would be better but I'm curious to hear your thoughts

Comment on lines +26 to +28
"hostRequirements": {
"gpu": "optional"
},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't know about this option! This is cool as it can still work when gpu is not available while supporting gpu!

Comment on lines +29 to +30
"workspaceMount": "source=${localWorkspaceFolder},target=/workspaces/repo,type=bind",
"workspaceFolder": "/workspaces/repo",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

Comment on lines +1 to +11
# Azure DevOps pipeline for CI (Microsoft-hosted version)
# As the Microsoft-hosted agent option has a limit of 10GB of storage for disk outputs from a pipeline,
# this causes an issue when the Docker images for modules under src require more than 10GB of storage.
# If you will run into space issues (or other limitations with a Microsoft hosted agent option outlined in
# https://learn.microsoft.com/en-us/azure/devops/pipelines/agents/hosted?view=azure-devops&tabs=yaml#capabilities-and-limitations),
# consider using the .azuredevops/ado-ci-pipeline-self-hosted.yml version or using scale set agents, see
# this link for more info: https://learn.microsoft.com/en-us/azure/devops/pipelines/agents/scale-set-agents?view=azure-devops
# Note that docker images will only be build for src directories that contain at least one test file, so the
# total space consumed by Docker builds will be dependent on which modules under src contain tests.
# For setting up the pipeline in ADO see:
# https://learn.microsoft.com/en-us/azure/devops/pipelines/agents/pools-queues?view=azure-devops&tabs=yaml%2Cbrowser
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the reason for diff showing up here? It looks the same?

Comment on lines +24 to +25
pool:
vmImage: 'ubuntu-latest'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see every stage uses the same pool. so should we stick with the previous design where we define it once for all?

# but could be adapated to other OSs.

# Azure DevOps CI Pipeline (Self-Hosted Agent)
#
Copy link
Member

@fujikosu fujikosu Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this 10GB part was the reason why we needed two different yamls. Could you check if it's ok to delete this part? If we still have this restriction, we likely need some cleanup method we had for self-hosted version specifically

- name: Checkout code
uses: actions/checkout@v6
- name: Checkout repository
uses: actions/checkout@v4
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why does this degrade version?

Comment on lines +132 to +155
# Run linter on codebase
lint:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Python 3.11
uses: actions/setup-python@v5
with:
name: test-and-coverage-results
path: |
**/test-reuslts-*.xml
coverage.xml
python-version: 3.11

- name: Install requirements
run: |
python -m venv venv
source venv/bin/activate
python -m pip install --upgrade pip
pip install -r requirements-dev.txt

- name: Run ruff linter
run: |
source venv/bin/activate
ruff check --output-format github
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be better to keep this at the previous position as this part would run faster and if we fail at linter, we don't need to proceed further and that'd help us iterate faster

filename: 'coverage/**/*.xml'

- name: Add code coverage summary markdown to github step summary
- name: Add code coverage summary to job summary
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is not accurate?

Comment on lines +71 to +90
# Smoke test for notebooks devcontainer
test-notebooks:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Setup .env file
run: cp .env.example .env

- name: Build and smoke test notebooks devcontainer
uses: devcontainers/ci@v0.3
with:
configFile: notebooks/.devcontainer/devcontainer.json
push: never
runCmd: |
cd /workspaces/repo
python --version
pytest --version
ruff --version
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we merge this to the previous section's build check?

@@ -13,44 +13,109 @@ permissions:
contents: read
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it's really worth splitting into several jobs instead of having having them as multiple steps based on job complextity we have. One thing I noticed is this does repo clone 4 times as it used to be just one for example and that's one of downsides of going with this structure

Copy link
Member

@fujikosu fujikosu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your PR! I left several comments so please check them

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants