If there’s no working_directory value in the step, you’ll start in the directory /home/circleci/project:
-checkout-run:name:Runninge2etestscommand:|
pwd
# prints /home/circleci/project
ls
# prints the files in your repo: babel.config.json LICENSE php etc…Code language:YAML(yaml)
That directory will also have your repo, assuming your checkout command didn’t have a path value.
If the checkout command had a path value, it’ll be checked out there:
-checkout:path:/tmp/e2e-run:name:Runninge2etestscommand:|
pwd
# prints /home/circleci/project
cd /tmp/e2e
ls
# prints babel.config.json LICENSE php etc…Code language:YAML(yaml)
If working_directory
If there’s a working_directory value in the step, you’ll start in that directory, relative to /home/circleci/project
-run:name:Runninge2etestsworking_directory:e2ecommand:| pwd # prints /home/circleci/project/e2eCode language:YAML(yaml)
Because that doesn’t start with /, it is relative to ~/project/
So it’ll be ~/project/e2e
If it started with / or ~/ it’d be an absolute path:
-run:name:Runninge2etestsworking_directory:/tmp/e2ecommand:| pwd # prints /tmp/e2eCode language:YAML(yaml)
Don’t Do This
-run:cdfoo-run:npmtestCode language:YAML(yaml)
Each run command goes back to /home/circleci/project or its working_directory:
-run:cdfoo&&pwd# prints /home/circleci/project/foo-run:pwd# prints /home/circleci/projectCode language:YAML(yaml)
Checking Out Repo To Another Directory
version:2.1references:REPO_PATH:&REPO_PATH/tmp/e2ejobs:js-build:docker:-image:cimg/node:14.18steps:-checkout:path:*REPO_PATH-run:working_directory:*REPO_PATHcommand:| pwd # prints /tmp/e2e-run:working_directory:*REPO_PATHcommand:npmtestCode language:YAML(yaml)
But you’ll only need REPO_PATH if you have multiple steps.
Usually, you can simply pass a string literal to working_directory.
And this should only be needed if you’re checking out multiple repos.
Like a repo for e2e tests, and a repo for your project.
Instant Feedback
Here’s how to cd into directory and see where you are, right away.
It simply returns a new CircleCI.Job for each php version passed.
No need to define parameters for the job, or read the docs about how to create a matrix.
Job Array
On line 21, this creates an array for the jobs.
At the end of this array, later in this post, we’ll call .forEach() to add each job to the config and workflow.
import * as fs from"fs";
import CircleCI from"@circleci/circleci-config-sdk";
const config = new CircleCI.Config();
const workflow = new CircleCI.Workflow("test-lint");
config.addWorkflow(workflow);
functioncreatePhpTestJobs(...phpVersions: string[]) {
return phpVersions.map((phpVersion) => {
returnnew CircleCI.Job(
`php-test-${phpVersion.replace(".", "-")}`,
new CircleCI.executors.DockerExecutor(`cimg/php:${phpVersion}`),
[
new CircleCI.commands.Checkout(),
new CircleCI.commands.Run({ command: "composer i && composer test" }),
]
);
});
}
[
...createPhpTestJobs("7.3", "7.4", "8.0", "8.1"),
new CircleCI.Job(
"php-lint",
new CircleCI.executors.DockerExecutor("cimg/php:8.1"),
[
new CircleCI.commands.Checkout(),
new CircleCI.commands.Run({ command: "composer i && composer lint" }),
]
),
Code language:TypeScript(typescript)
On line 22, we spread the php-test jobs from the function above into the jobs array.
Then, on line 23, we add a new job.
Like before, the CircleCI.Job constructor accepts the job name, the executor, and the steps.
The first command in each is Checkout().
On line 28, the constructor for CircleCI.commands.Run doesn’t need a name value.
Last 2 Jobs
On line 31, this adds more jobs:
import * as fs from"fs";
import CircleCI from"@circleci/circleci-config-sdk";
const config = new CircleCI.Config();
const workflow = new CircleCI.Workflow("test-lint");
config.addWorkflow(workflow);
functioncreatePhpTestJobs(...phpVersions: string[]) {
return phpVersions.map((phpVersion) => {
returnnew CircleCI.Job(
`php-test-${phpVersion.replace(".", "-")}`,
new CircleCI.executors.DockerExecutor(`cimg/php:${phpVersion}`),
[
new CircleCI.commands.Checkout(),
new CircleCI.commands.Run({ command: "composer i && composer test" }),
]
);
});
}
[
...createPhpTestJobs("7.3", "7.4", "8.0", "8.1"),
new CircleCI.Job(
"php-lint",
new CircleCI.executors.DockerExecutor("cimg/php:8.1"),
[
new CircleCI.commands.Checkout(),
new CircleCI.commands.Run({ command: "composer i && composer lint" }),
]
),
new CircleCI.Job(
"js-build",
new CircleCI.executors.DockerExecutor("cimg/node:14.18"),
[
new CircleCI.commands.Checkout(),
new CircleCI.commands.Run({ command: "npm ci" }),
new CircleCI.commands.Run({
name: "Running JS linting and unit test",
command: "npm run lint:js && npm run test:js",
}),
]
),
new CircleCI.Job(
"e2e-test",
new CircleCI.executors.MachineExecutor("large", "ubuntu-2004:202111-02"),
[
new CircleCI.commands.Checkout(),
new CircleCI.commands.Run({ command: "npm ci" }),
new CircleCI.commands.Run({
name: "Running e2e tests",
command: "npm run wp-env start && npm run test:e2e",
}),
new CircleCI.commands.StoreArtifacts({ path: "artifacts" }),
]
),
].forEach((job) => {
config.addJob(job);
workflow.addJob(job);
});
fs.writeFile("./dynamicConfig.yml", config.stringify(), () => {});
Code language:TypeScript(typescript)
And on line 53, this stores test errors in artifacts, so we can debug.
This calls forEach() on line 56, adding each job to the config and workflow.
Generated .yml File
On line 61, this writes the config to .circleci/dynamic/dynamicConfig.yml.
You don’t have to commit this, the index.ts file generates it on every pipeline run:
.circleci/dynamic/dynamicConfig.yml
# This configuration has been automatically generated by the CircleCI Config SDK.# For more information, see https://github.com/CircleCI-Public/circleci-config-sdk-ts# SDK Version: 0.0.0-developmentversion:2.1setup:falsejobs:php-test-7-3:docker:-image:cimg/php:7.3resource_class:mediumsteps:-checkout-run:command:composeri&&composertestphp-test-7-4:docker:-image:cimg/php:7.4resource_class:mediumsteps:-checkout-run:command:composeri&&composertestphp-test-8-0:docker:-image:cimg/php:8.0resource_class:mediumsteps:-checkout-run:command:composeri&&composertestphp-test-8-1:docker:-image:cimg/php:8.1resource_class:mediumsteps:-checkout-run:command:composeri&&composertestphp-lint:docker:-image:cimg/php:8.1resource_class:mediumsteps:-checkout-run:command:composeri&&composerlintjs-build:docker:-image:cimg/node:14.18resource_class:mediumsteps:-checkout-run:command:npmci-run:name:RunningJSlintingandunittestcommand:npmrunlint:js&&npmruntest:jse2e-test:machine:image:ubuntu-2004:202111-02resource_class:largesteps:-checkout-run:command:npmci-run:name:Runninge2etestscommand:npmrunwp-envstart&&npmruntest:e2e-store_artifacts:path:artifactsworkflows:test-lint:jobs:-php-test-7-3-php-test-7-4-php-test-8-0-php-test-8-1-php-lint-js-build-e2e-testCode language:YAML(yaml)
This .yml file is 81 lines, where index.ts is 61.
Settings
Finally, go to your repo’s CircleCI Advanced Settings:
Please also ensure Docker is running on your machine.
Running the CLI
$ circleci local execute --job deployCode language:Bash(bash)
This will run the job on your machine:
You might see that pulling some images to run the job.
Getting the Job Image Name
Open a new terminal tab, and run this:
$ docker psCode language:Bash(bash)
That should show the image that’s running the job:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b6e09b7009ab cimg/base:current "/bin/sh" 13 seconds ago Up 10 seconds naughty_jones
b992e2d2c0a8 circleci/picard "/opt/circleci/linux…" 16 seconds ago Up 16 seconds naughty_colden
5c233ff5c3b9 cimg/base:current "/bin/sh" 6 minutes ago Up 5 minutes elastic_brattain
Code language:Bash(bash)
Look for an image right above circleci/picard.
If you don’t see circleci/picard, you’ll probably have to wait 10-20 seconds while the circleci local command above pulls that image.
In this case, it’s the cimg/base:current image.
Copy the container ID of that image to use it below.
In this example, it’s b6e09b7009ab.
That’s the image where your job is running, and you’re going to get bash access to it.