A command-line utility to trigger builds in TeamCity to run provider acceptance tests. Given a PR number it can find the files modified, discover the tests to run, and generate a TEST_PATTERN automatically.
go install github.com/katbyte/tctest@latestAll options can be passed as command-line flags, environment variables, or via a configuration file.
You can place a .tctest file in your home directory ~/.tctest (for global settings) or in your current directory ./.tctest (for repository-specific settings). Keys in the file match the long flag names or environment variables using the env format. For example:
SERVER=ci.katbyte.net
BUILD-TYPE-ID=AzureRm
TOKEN-GH=github_pat_1234
TOKEN-TC=ey...Create a file like set_env_example.sh and source it for environment variables.
| Variable | Flag | Description |
|---|---|---|
TCTEST_SERVER |
--server, -s |
TeamCity server URL |
TCTEST_BUILD_TYPE_ID |
--build-type-id, -b |
TeamCity build configuration ID |
TCTEST_TOKEN_TC |
--token-tc, -t |
TeamCity authentication token |
TCTEST_USER |
--username |
TeamCity username (alternative to token) |
TCTEST_PASS |
--password |
TeamCity password (alternative to token) |
TCTEST_PROPERTIES |
--properties, -p |
Default build parameters in KEY=VALUE;KEY2=VALUE2 format |
GITHUB_TOKEN |
--token-gh |
GitHub OAuth token |
TCTEST_REPO |
--repo, -r |
GitHub repository (e.g. hashicorp/terraform-provider-azurerm) |
TCTEST_FILEREGEX |
--fileregex |
Regex to filter PR files for test discovery |
TCTEST_ACCTEST_FILE_SUFFIX_REGEXES |
--acctest-file-suffix-regexes |
Comma-separated regex suffix (without .go) to find relevant acceptance-test files for a resource. |
TCTEST_SPLIT_TESTS_ON |
--splitteston |
Character to split test names on (default: _) |
TCTEST_REAPPEND_SPLIT_CHARACTER |
--reappend-split-character |
Whether to append the split character to the resulting test filter for more precise filtering |
TCTEST_INDIVIDUAL |
--individual, -i |
Discover individual test functions by full name instead of split prefixes: directly changed test files yield only the tests the PR modifies, indirectly discovered files yield every test they contain |
TCTEST_WAIT |
--wait, -w |
Wait for builds to complete |
TCTEST_LATESTBUILD |
--latest |
Get the latest build |
TCTEST_SKIP_QUEUE |
--skip-queue, -q |
Put the build to the top of the queue |
TCTEST_OPEN_BROWSER |
--open, -o |
Open PR and build URLs in the browser |
TCTEST_BUILD_TAGS |
--tag |
Build tags to add to triggered builds |
TCTEST_COMMENT |
--comment, -c |
Post a GitHub comment with test results |
TCTEST_FORCE_OLD_UI |
--build-link-force-old-ui |
Force build URLs to use the classic TeamCity UI |
TCTEST_OUTPUT_QUIET |
--quiet |
Minimal machine-readable output |
TCTEST_OUTPUT_JSON |
--json |
Output build results as a JSON array |
TCTEST_OUTPUT_SILENT |
--silent |
Suppress all output |
TCTEST_LOCAL_REPO_PATH |
--local-repo-path |
Path to a local git clone for AST-based test detection (enables import tracing, and changes default mode to AST) |
TCTEST_MODE |
--mode |
Local detection mode: api (default) or AST (default when --local-repo-path is provided) |
TCTEST_LOCAL_VENDOR_MODE |
--local-vendor-mode |
Vendor tracing mode: basic (default) or none |
Triggers a TeamCity build for the given branch with the specified test regex passed as TEST_PATTERN/TEST_PREFIX.
# with flags
tctest branch master TestAcc -s ci.katbyte.me -b AzureRm
# with environment variables set
tctest branch master TestAcc
# alias
tctest b master TestAccDiscovers tests from modified PR files and triggers builds. If a test_regex is provided as the second argument, it overrides auto-discovery and is sent directly as TEST_PATTERN/TEST_PREFIX to TeamCity.
# auto-discover tests from PR files
tctest pr 3232
# specify a test pattern manually
tctest pr 3232 TestAccAzureRMVirtualNetwork
# multiple PRs at once
tctest pr 3232,5454,7676
# wait for builds to complete and show results
tctest pr 3232 --wait
# open PR and build in browser
tctest pr 3232 --openUse --service to target specific service(s). The behavior depends on whether a test_regex or --all is also provided:
--servicealone: Discovers tests from PR files, then filters to only trigger builds for the named services. If the PR doesn't touch those services, no builds are triggered.--service+test_regex: Skips discovery and triggers the given regex directly for each named service.--service+--all: Skips discovery and triggersTestAccfor each named service.
Use all as the service name to target every service in the repo.
# discover tests from PR, but only trigger for the network service
tctest pr 3232 --service network
# discover tests from PR for multiple services
tctest pr 3232 --service network,compute
# run a specific test on a specific service (no discovery)
tctest pr 3232 --service network TestAccVirtualNetwork_basic
# run ALL tests for a specific service (no discovery)
tctest pr 3232 --service network --all
# run ALL tests for ALL services (no discovery)
# note: triggers one build per service, so --max-builds-per-pr (default 7) applies; bypass it with -f/--force
tctest pr 3232 --service all --all --force
# invalid service names will error with a list of valid services
tctest pr 3232 --service fakesvc
# ERROR: invalid service(s): fakesvc
# valid services: aadb2c, advisor, apimanagement, ...Without --service, --all overrides the discovered test regex with TestAcc to run all tests for the affected services:
tctest pr 3232 --allAn explicit test_regex, --all, and --add-tests are mutually exclusive — they are three different ways of specifying which tests to run, so combining them is an error.
Adds POST_GITHUB_COMMENT=true to the build properties, telling TeamCity to post test results as a comment on the PR:
tctest pr 3232 --comment
tctest pr 3232 -cDiscovers all open PRs matching specified filters and triggers builds for each. If a test_regex is provided as the first argument, it overrides auto-discovery and is sent directly as TEST_PATTERN/TEST_PREFIX for every matching PR.
# all open PRs by specific authors
tctest prs -a katbyte,author2
# PRs with specific labels (all must match)
tctest prs -l needs-testing,service/network
# PRs with any matching label
tctest prs --f-labels-any needs-testing,ready-for-review
# PRs by author with a specific label
tctest prs -a katbyte -l needs-testing
# PRs not in draft
tctest prs -d
# PRs created within the last 24 hours
tctest prs --f-created-time 24h
# PRs updated within the last 2 hours
tctest prs --f-updated-time 2h
# PRs with a specific milestone
tctest prs -m v3.0.0
# PRs without a specific milestone
tctest prs -m -v3.0.0
# PRs matching a title regex (case-insensitive)
tctest prs --f-title-regex "network.*fix"
# combine filters with a custom test pattern
tctest prs TestAccAzureRM -a katbyte -l needs-testing| Flag | Short | Description |
|---|---|---|
--f-authors |
-a |
Only test PRs by these authors (comma-separated) |
--f-labels-all |
-l |
Only test PRs matching all label conditions. Prefix with - to negate |
--f-labels-any |
Only test PRs matching any label condition. Prefix with - to negate |
|
--f-milestone |
-m |
Filter by milestone. Prefix with - to exclude |
--f-drafts |
-d |
Filter out draft PRs |
--f-created-time |
Only PRs created within this duration (e.g. 24h, 7d) |
|
--f-updated-time |
Only PRs updated within this duration | |
--f-title-regex |
Filter PRs by title using case-insensitive regex |
Lists the tests that would be triggered for a PR without actually starting a build.
tctest list 3232 Defaults work for both AzureRM and AWS out of the box. In most of the cases just set repositry flag.
tctest list 3232 -r hashicorp/terraform-provider-awsFor custom usecases, you can override --fileregex and --acctest-file-suffix-regexes flags.
run tctest --help to see their defaults.
By default a changed test file contributes all of its tests as split prefixes (e.g. TestAccPostgresqlFlexibleServer,
which matches the whole test family). With --individual/-i, tctest reads the PR diff and narrows directly changed
test files to just the test functions the PR actually modifies, listed by their full names:
tctest list -i 3232
# TestAccPostgresqlFlexibleServer_completeWith -i, prefixes are never used: test files discovered indirectly (e.g. via a changed resource file, where any of
the resource's tests could be affected) list every test function they contain, by full name. The flag also works with
pr and prs to trigger only the discovered tests.
# show PASS/FAIL/SKIP results
tctest results 12345
# wait for a running build to complete, then show results
tctest results 12345 --wait# show results for all builds for a PR
tctest results pr 12345
# show results for only the latest build
tctest results pr 12345 --latest
# wait for builds to complete, then show results
tctest results pr 12345 --waittctest versionThese flags apply to any command that triggers a build:
| Flag | Short | Description |
|---|---|---|
--properties |
-p |
Build parameters in KEY=VALUE;KEY2=VALUE2 format |
--comment |
-c |
Post a GitHub comment with test results (POST_GITHUB_COMMENT=true) |
--skip-queue |
-q |
Put the build to the top of the queue |
--wait |
-w |
Wait for the build to complete before exiting |
--tag |
Add tags to the triggered build (comma-separated) | |
--queue-timeout |
Minutes to wait for a queued build to start (default: 60) | |
--run-timeout |
Minutes to wait for a running build to finish (default: 60) | |
--open |
-o |
Open the PR and build URL in the browser |
--build-link-force-old-ui |
Append &fromSakuraUI=true to build URLs to force the classic TeamCity UI |
By default tctest prints colorized, verbose output. Use these flags to control output:
| Flag | Description |
|---|---|
| (default) | Full colorized output with test discovery details, file listings, and build info |
--verbose, -v |
Show all file listings (even when collapsed) and detailed trace output |
--quiet |
One line per build: PR@SERVICE@BUILDID URL |
--json |
JSON array of all triggered builds (output at end) |
--silent |
Suppress all output (errors still print to stderr) |
--dry-run |
Show what builds would be triggered without actually triggering them |
32181@costmanagement@658292 https://hashicorp.teamcity.com/viewQueued.html?itemId=658292
32181@mssql@658293 https://hashicorp.teamcity.com/viewQueued.html?itemId=658293
[
{
"pr": 32181,
"service": "costmanagement",
"build_number": 658292,
"url": "https://hashicorp.teamcity.com/viewQueued.html?itemId=658292"
},
{
"pr": 32181,
"service": "mssql",
"build_number": 658293,
"url": "https://hashicorp.teamcity.com/viewQueued.html?itemId=658293"
}
]When no test regex is provided, tctest automatically discovers tests by:
- Listing all files modified in the PR
- Filtering to files in
internal/service/<service_name>/<file_name>.godirectory only (configurable via--fileregex) - Deriving test file names (e.g.
resource_foo.go→resource_foo_test.go) - Also discovering related test files (e.g.
resource_foo_list_test.go,resource_foo_data_source_test.go) - Downloading test files and extracting test function names using Go AST parsing
- Grouping tests by service and triggering a separate build per service
Files in /client/, /parse/, /validate/ subdirectories and registration.go/resourceids.go are automatically skipped. Deleted files are also excluded.
Discovered tests are grouped by service and displayed with padded service names for alignment:
cognitive : TestAccCognitiveDeployment, TestAccCognitiveAccountProject, TestAccCognitiveAccountProjectDataSource, TestAccCognitiveAccount
eventhub : TestAccEventHubConsumerGroupDataSource, TestAccEventHubConsumerGroup
batch : TestAccBatchAccount, TestAccBatchPoolDataSource, TestAccBatchPool, TestAccBatchApplicationDataSource, TestAccBatchApplication, TestAccBatchAccountDataSource
By default, changed files and test files are listed in the output. When either list exceeds 20 files (configurable via --collapse-files-after), the list is collapsed to just a count with a hint:
changed files: 5
internal/services/cognitive/cognitive_deployment_resource.go [RESOURCE]
...
test files: 61
61 exceeds display limit of 20, use -v or --collapse-files-after 0 to see all
Use --verbose (-v) to always show all files regardless of the threshold, or --collapse-files-after 0 to disable collapsing entirely.
For more accurate test discovery — especially when helper, validation, or client files are modified — point tctest at a local clone of the repository:
tctest list 3232 --local-repo-path /path/to/local/clone
tctest pr 3232 --local-repo-path /path/to/local/cloneWhen --local-repo-path is set, tctest:
- Fetches the PR merge ref (
git fetch origin pull/{N}/merge) and checks outFETCH_HEAD - Uses the local filesystem instead of HTTP downloads (no API rate limits, no 1000-file directory cap)
- Traces imports from helper files back to resource files to discover affected tests
If a PR modifies a non-resource .go file in the same package (e.g. internal/services/cognitive/common.go), tctest:
- Extracts all symbols defined in the helper
- Scans resource files in the same directory for references to those symbols
- Discovers test files for any matched resource files
- Labels these tests as
[TRACED]in the output
If a PR modifies a file in a sub-directory (e.g. internal/services/network/parse/helpers.go), tctest:
- Parses the Go imports of all files in the parent service directory
- Finds resource files that import the helper package and reference changed exported symbols
- Performs BFS traversal through intermediate packages up to
--local-trace-depthlevels - Labels these tests as
[TRACED]in the output
If a PR modifies files under vendor/, tctest can trace which resource files import those vendor packages:
# enabled by default
tctest pr 3232 --local-repo-path /path/to/clone --local-vendor-mode basic
# disable vendor tracing
tctest pr 3232 --local-repo-path /path/to/clone --local-vendor-mode noneTests discovered via vendor tracing are labeled [VENDOR] in the output.
With --verbose (-v), tctest shows the detailed trace results — which helper file traced to which resource files:
tracing symbols from 1 same-package helper file(s)...
internal/services/appconfiguration/app_configuration.go →
internal/services/appconfiguration/app_configuration_data_source.go
internal/services/appconfiguration/app_configuration_resource.go
tracing symbols from 3 cross-package helper file(s)...
internal/services/batch/validate/account_name.go →
internal/services/batch/batch_account_resource.go
internal/services/batch/batch_application_resource.go
Without --verbose, the summary line shows just the count of traced resource files:
tracing symbols from 1 same-package helper file(s)... 4 resource file(s)
tracing symbols from 3 cross-package helper file(s)... 20 resource file(s)
tracing imports from 3 vendor file(s)... 15 resource file(s)
| Flag | Default | Description |
|---|---|---|
--fileregex |
^internal/services?/... |
Regex to filter PR files for test discovery |
--splitteston |
_ |
Character to split test names on |
--acctest-file-suffix-regexes |
(multiple) | Comma-separated regex patterns to match test file suffixes |
--reappend-split-character |
false |
Append the split character to the test filter for more precise matching |
--concurrency |
5 |
Maximum concurrent file downloads during test discovery |
--local-repo-path |
(empty) | Path to a local git clone for AST-based detection (changes default mode to AST) |
--mode |
AST |
Mode for local detection: api (default) or AST (default when --local-repo-path is provided) |
--local-trace-depth |
10 |
Max BFS depth for import tracing (0 to disable) |
--local-vendor-mode |
basic |
Vendor tracing mode: basic (import-based) or none (disabled) |
--collapse-files-after |
20 |
Collapse file lists when count exceeds this value (0 to always show) |
--verbose, -v |
false |
Show detailed file listings and trace output |
Note: The
--local-repo-pathclone will have its working tree modified (checkout of FETCH_HEAD). Use a dedicated clone for tctest, not your active working directory. tctest will abort if the clone has uncommitted changes.