From 8ace241f16b88d12d821bc17ef1040bc01a5b5ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20Sol=C3=A1r?= Date: Fri, 23 Jan 2026 12:24:43 +0100 Subject: [PATCH 01/10] feat(builds): adds build tag commands --- package.json | 2 +- src/commands/builds/_index.ts | 4 + src/commands/builds/add-tag.ts | 92 ++++++++++++ src/commands/builds/remove-tag.ts | 107 ++++++++++++++ test/api/commands/builds/tags.test.ts | 201 ++++++++++++++++++++++++++ yarn.lock | 24 ++- 6 files changed, 427 insertions(+), 3 deletions(-) create mode 100644 src/commands/builds/add-tag.ts create mode 100644 src/commands/builds/remove-tag.ts create mode 100644 test/api/commands/builds/tags.test.ts diff --git a/package.json b/package.json index 3cc16273f..7d9792ccc 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "@skyra/jaro-winkler": "^1.1.1", "adm-zip": "~0.5.15", "ajv": "~8.17.1", - "apify-client": "^2.14.0", + "apify-client": "2.21.1-beta.11", "archiver": "~7.0.1", "axios": "^1.11.0", "chalk": "~5.6.0", diff --git a/src/commands/builds/_index.ts b/src/commands/builds/_index.ts index 8cc466903..936397bb4 100644 --- a/src/commands/builds/_index.ts +++ b/src/commands/builds/_index.ts @@ -1,8 +1,10 @@ import { ApifyCommand } from '../../lib/command-framework/apify-command.js'; +import { BuildsAddTagCommand } from './add-tag.js'; import { BuildsCreateCommand } from './create.js'; import { BuildsInfoCommand } from './info.js'; import { BuildsLogCommand } from './log.js'; import { BuildsLsCommand } from './ls.js'; +import { BuildsRemoveTagCommand } from './remove-tag.js'; import { BuildsRmCommand } from './rm.js'; export class BuildsIndexCommand extends ApifyCommand { @@ -12,6 +14,8 @@ export class BuildsIndexCommand extends ApifyCommand static override subcommands = [ // + BuildsAddTagCommand, + BuildsRemoveTagCommand, BuildsRmCommand, BuildsLsCommand, BuildsLogCommand, diff --git a/src/commands/builds/add-tag.ts b/src/commands/builds/add-tag.ts new file mode 100644 index 000000000..fc0779cae --- /dev/null +++ b/src/commands/builds/add-tag.ts @@ -0,0 +1,92 @@ +import type { ActorTaggedBuild, ApifyApiError } from 'apify-client'; +import chalk from 'chalk'; + +import { ApifyCommand } from '../../lib/command-framework/apify-command.js'; +import { Flags } from '../../lib/command-framework/flags.js'; +import { error, success } from '../../lib/outputs.js'; +import { getLoggedClientOrThrow } from '../../lib/utils.js'; + +export class BuildsAddTagCommand extends ApifyCommand { + static override name = 'add-tag' as const; + + static override description = 'Adds a tag to a specific Actor build.'; + + static override flags = { + build: Flags.string({ + char: 'b', + description: 'The build ID to tag.', + required: true, + }), + tag: Flags.string({ + char: 't', + description: 'The tag to add to the build.', + required: true, + }), + }; + + async run() { + const { build: buildId, tag } = this.flags; + + const apifyClient = await getLoggedClientOrThrow(); + + const build = await apifyClient.build(buildId).get(); + + if (!build) { + error({ message: `Build with ID "${buildId}" was not found on your account.`, stdout: true }); + return; + } + + if (build.status !== 'SUCCEEDED') { + error({ + message: `Build with ID "${buildId}" has status "${build.status}". Only successful builds can be tagged.`, + stdout: true, + }); + return; + } + + const actor = await apifyClient.actor(build.actId).get(); + + if (!actor) { + error({ message: `Actor with ID "${build.actId}" was not found.`, stdout: true }); + return; + } + + // Check if this tag already points to the same build + const existingTaggedBuilds = (actor.taggedBuilds ?? {}) as Record; + const existingTagData = existingTaggedBuilds[tag]; + + if (existingTagData?.buildId === buildId) { + error({ + message: `Build "${buildId}" is already tagged as "${tag}".`, + stdout: true, + }); + return; + } + + try { + // Update only the specific tag + await apifyClient.actor(build.actId).update({ + taggedBuilds: { + [tag]: { + buildId: build.id, + }, + }, + }); + + const previousBuildInfo = existingTagData?.buildNumber + ? ` (previously pointed to build ${chalk.gray(existingTagData.buildNumber)})` + : ''; + + success({ + message: `Tag "${chalk.yellow(tag)}" added to build ${chalk.gray(build.buildNumber)} (${chalk.gray(buildId)})${previousBuildInfo}`, + stdout: true, + }); + } catch (err) { + const casted = err as ApifyApiError; + error({ + message: `Failed to add tag "${tag}" to build "${buildId}".\n ${casted.message || casted}`, + stdout: true, + }); + } + } +} diff --git a/src/commands/builds/remove-tag.ts b/src/commands/builds/remove-tag.ts new file mode 100644 index 000000000..6ca252e8e --- /dev/null +++ b/src/commands/builds/remove-tag.ts @@ -0,0 +1,107 @@ +import type { ActorTaggedBuild, ApifyApiError } from 'apify-client'; +import chalk from 'chalk'; + +import { ApifyCommand } from '../../lib/command-framework/apify-command.js'; +import { Flags } from '../../lib/command-framework/flags.js'; +import { useYesNoConfirm } from '../../lib/hooks/user-confirmations/useYesNoConfirm.js'; +import { error, info, success } from '../../lib/outputs.js'; +import { getLoggedClientOrThrow } from '../../lib/utils.js'; + +export class BuildsRemoveTagCommand extends ApifyCommand { + static override name = 'remove-tag' as const; + + static override description = 'Removes a tag from a specific Actor build.'; + + static override flags = { + build: Flags.string({ + char: 'b', + description: 'The build ID to remove the tag from.', + required: true, + }), + tag: Flags.string({ + char: 't', + description: 'The tag to remove from the build.', + required: true, + }), + yes: Flags.boolean({ + char: 'y', + description: 'Automatic yes to prompts; assume "yes" as answer to all prompts.', + default: false, + }), + }; + + async run() { + const { build: buildId, tag, yes } = this.flags; + + const apifyClient = await getLoggedClientOrThrow(); + + const build = await apifyClient.build(buildId).get(); + + if (!build) { + error({ message: `Build with ID "${buildId}" was not found on your account.`, stdout: true }); + return; + } + + const actor = await apifyClient.actor(build.actId).get(); + + if (!actor) { + error({ message: `Actor with ID "${build.actId}" was not found.`, stdout: true }); + return; + } + + const existingTaggedBuilds = (actor.taggedBuilds ?? {}) as Record; + const existingTagData = existingTaggedBuilds[tag]; + + // Check if the tag exists + if (!existingTagData) { + error({ + message: `Tag "${tag}" does not exist on Actor "${actor.name}".`, + stdout: true, + }); + return; + } + + // Check if the tag points to this build + if (existingTagData.buildId !== buildId) { + error({ + message: `Tag "${tag}" is not associated with build "${buildId}". It points to build "${existingTagData.buildNumber}" (${existingTagData.buildId}).`, + stdout: true, + }); + return; + } + + // Confirm removal + const confirmed = await useYesNoConfirm({ + message: `Are you sure you want to remove tag "${chalk.yellow(tag)}" from build ${chalk.gray(build.buildNumber)}?`, + providedConfirmFromStdin: yes || undefined, + }); + + if (!confirmed) { + info({ + message: `Tag removal was canceled.`, + stdout: true, + }); + return; + } + + try { + // To remove a tag, set it to null + await apifyClient.actor(build.actId).update({ + taggedBuilds: { + [tag]: null, + }, + } as never); + + success({ + message: `Tag "${chalk.yellow(tag)}" removed from build ${chalk.gray(build.buildNumber)} (${chalk.gray(buildId)})`, + stdout: true, + }); + } catch (err) { + const casted = err as ApifyApiError; + error({ + message: `Failed to remove tag "${tag}" from build "${buildId}".\n ${casted.message || casted}`, + stdout: true, + }); + } + } +} diff --git a/test/api/commands/builds/tags.test.ts b/test/api/commands/builds/tags.test.ts new file mode 100644 index 000000000..bb9a3afd6 --- /dev/null +++ b/test/api/commands/builds/tags.test.ts @@ -0,0 +1,201 @@ +import type { Actor, Build } from 'apify-client'; + +import { testRunCommand } from '../../../../src/lib/command-framework/apify-command.js'; +import { waitForBuildToFinish } from '../../../__setup__/build-utils.js'; +import { testUserClient } from '../../../__setup__/config.js'; +import { safeLogin, useAuthSetup } from '../../../__setup__/hooks/useAuthSetup.js'; +import { useConsoleSpy } from '../../../__setup__/hooks/useConsoleSpy.js'; +import { useUniqueId } from '../../../__setup__/hooks/useUniqueId.js'; + +const { BuildsAddTagCommand } = await import('../../../../src/commands/builds/add-tag.js'); +const { BuildsRemoveTagCommand } = await import('../../../../src/commands/builds/remove-tag.js'); + +const ACTOR_NAME = useUniqueId('cli-builds-tag-test'); + +useAuthSetup({ perTest: false }); + +const { lastErrorMessage, lastLogMessage } = useConsoleSpy(); + +const TEST_TIMEOUT = 120_000; + +describe('[api] apify builds add-tag / remove-tag', () => { + let testActor: Actor; + let testBuild: Build; + + beforeAll(async () => { + await safeLogin(); + + // Create a test actor with a simple source + testActor = await testUserClient.actors().create({ + name: ACTOR_NAME, + versions: [ + { + versionNumber: '0.0', + sourceType: 'SOURCE_FILES', + buildTag: 'latest', + sourceFiles: [ + { + name: 'Dockerfile', + format: 'TEXT', + content: 'FROM apify/actor-node:20\nCOPY . ./\nCMD ["node", "main.js"]', + }, + { + name: 'main.js', + format: 'TEXT', + content: 'console.log("Hello");', + }, + ], + }, + ], + }); + + // Build the actor and wait for it to finish + const buildStarted = await testUserClient.actor(testActor.id).build('0.0'); + testBuild = (await waitForBuildToFinish(testUserClient, buildStarted.id))!; + }, TEST_TIMEOUT); + + afterAll(async () => { + if (testActor) { + await testUserClient.actor(testActor.id).delete(); + } + }); + + describe('builds add-tag', () => { + it('should add a tag to a build', async () => { + await testRunCommand(BuildsAddTagCommand, { + flags_build: testBuild.id, + flags_tag: 'beta', + }); + + expect(lastLogMessage()).toMatch(/tag.*beta.*added/i); + + // Verify via API + const actor = await testUserClient.actor(testActor.id).get(); + expect(actor?.taggedBuilds?.beta?.buildId).toBe(testBuild.id); + }); + + it('should show error when build is already tagged with the same tag', async () => { + // First, ensure the tag exists + await testRunCommand(BuildsAddTagCommand, { + flags_build: testBuild.id, + flags_tag: 'existing-tag', + }); + + // Try to add the same tag again + await testRunCommand(BuildsAddTagCommand, { + flags_build: testBuild.id, + flags_tag: 'existing-tag', + }); + + expect(lastErrorMessage()).toMatch(/already tagged/i); + }); + + it('should show error when build does not exist', async () => { + await testRunCommand(BuildsAddTagCommand, { + flags_build: 'nonexistent-build-id', + flags_tag: 'test-tag', + }); + + expect(lastErrorMessage()).toMatch(/not found/i); + }); + + it( + 'should show previous build info when reassigning a tag', + async () => { + // Tag the build with 'reassign-test' + await testRunCommand(BuildsAddTagCommand, { + flags_build: testBuild.id, + flags_tag: 'reassign-test', + }); + + // Create another build + const buildStarted2 = await testUserClient.actor(testActor.id).build('0.0'); + const testBuild2 = (await waitForBuildToFinish(testUserClient, buildStarted2.id))!; + + // Reassign the tag to the new build + await testRunCommand(BuildsAddTagCommand, { + flags_build: testBuild2.id, + flags_tag: 'reassign-test', + }); + + expect(lastLogMessage()).toMatch(/previously pointed to build/i); + + // Verify via API + const actor = await testUserClient.actor(testActor.id).get(); + expect(actor?.taggedBuilds?.['reassign-test']?.buildId).toBe(testBuild2.id); + }, + TEST_TIMEOUT, + ); + }); + + describe('builds remove-tag', () => { + it('should remove a tag from a build', async () => { + // First add a tag + await testRunCommand(BuildsAddTagCommand, { + flags_build: testBuild.id, + flags_tag: 'to-remove', + }); + + // Verify it was added + let actor = await testUserClient.actor(testActor.id).get(); + expect(actor?.taggedBuilds?.['to-remove']?.buildId).toBe(testBuild.id); + + // Remove the tag with --yes flag to skip confirmation + await testRunCommand(BuildsRemoveTagCommand, { + flags_build: testBuild.id, + flags_tag: 'to-remove', + flags_yes: true, + }); + + expect(lastLogMessage()).toMatch(/tag.*to-remove.*removed/i); + + // Verify via API + actor = await testUserClient.actor(testActor.id).get(); + expect(actor?.taggedBuilds?.['to-remove']).toBeUndefined(); + }); + + it('should show error when tag does not exist', async () => { + await testRunCommand(BuildsRemoveTagCommand, { + flags_build: testBuild.id, + flags_tag: 'nonexistent-tag', + flags_yes: true, + }); + + expect(lastErrorMessage()).toMatch(/does not exist/i); + }); + + it( + 'should show error when tag points to a different build', + async () => { + // Create another build and tag it + const buildStarted2 = await testUserClient.actor(testActor.id).build('0.0'); + const testBuild2 = (await waitForBuildToFinish(testUserClient, buildStarted2.id))!; + + await testRunCommand(BuildsAddTagCommand, { + flags_build: testBuild2.id, + flags_tag: 'other-build-tag', + }); + + // Try to remove the tag using the first build's ID + await testRunCommand(BuildsRemoveTagCommand, { + flags_build: testBuild.id, + flags_tag: 'other-build-tag', + flags_yes: true, + }); + + expect(lastErrorMessage()).toMatch(/not associated with build/i); + }, + TEST_TIMEOUT, + ); + + it('should show error when build does not exist', async () => { + await testRunCommand(BuildsRemoveTagCommand, { + flags_build: 'nonexistent-build-id', + flags_tag: 'test-tag', + flags_yes: true, + }); + + expect(lastErrorMessage()).toMatch(/not found/i); + }); + }); +}); diff --git a/yarn.lock b/yarn.lock index fbce5a89c..4020f69ca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2300,7 +2300,7 @@ __metadata: adm-zip: "npm:~0.5.15" ajv: "npm:~8.17.1" apify: "npm:^3.2.4" - apify-client: "npm:^2.14.0" + apify-client: "npm:2.21.1-beta.11" archiver: "npm:~7.0.1" axios: "npm:^1.11.0" chalk: "npm:~5.6.0" @@ -2348,7 +2348,27 @@ __metadata: languageName: unknown linkType: soft -"apify-client@npm:^2.14.0, apify-client@npm:^2.17.0": +"apify-client@npm:2.21.1-beta.11": + version: 2.21.1-beta.11 + resolution: "apify-client@npm:2.21.1-beta.11" + dependencies: + "@apify/consts": "npm:^2.42.0" + "@apify/log": "npm:^2.2.6" + "@apify/utilities": "npm:^2.23.2" + "@crawlee/types": "npm:^3.3.0" + ansi-colors: "npm:^4.1.1" + async-retry: "npm:^1.3.3" + axios: "npm:^1.6.7" + content-type: "npm:^1.0.5" + ow: "npm:^0.28.2" + proxy-agent: "npm:^6.5.0" + tslib: "npm:^2.5.0" + type-fest: "npm:^4.0.0" + checksum: 10c0/60177986b424fc98eb7c3487f8f7958c1b5d7419deea8ea3852dd57ef971573a91f9f541f8c6cc4771a137376f110669f290ec763e0bdac06167de9a16213883 + languageName: node + linkType: hard + +"apify-client@npm:^2.17.0": version: 2.21.0 resolution: "apify-client@npm:2.21.0" dependencies: From eec0e5a8e88349e7cb55eaf3c961e1ef1808d821 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20Sol=C3=A1r?= Date: Sun, 25 Jan 2026 10:40:46 +0100 Subject: [PATCH 02/10] refactor(builds): changes error to warning --- src/commands/builds/add-tag.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/commands/builds/add-tag.ts b/src/commands/builds/add-tag.ts index fc0779cae..be4ac0904 100644 --- a/src/commands/builds/add-tag.ts +++ b/src/commands/builds/add-tag.ts @@ -3,7 +3,7 @@ import chalk from 'chalk'; import { ApifyCommand } from '../../lib/command-framework/apify-command.js'; import { Flags } from '../../lib/command-framework/flags.js'; -import { error, success } from '../../lib/outputs.js'; +import { error, success, warning } from '../../lib/outputs.js'; import { getLoggedClientOrThrow } from '../../lib/utils.js'; export class BuildsAddTagCommand extends ApifyCommand { @@ -56,7 +56,7 @@ export class BuildsAddTagCommand extends ApifyCommand Date: Sun, 25 Jan 2026 10:50:40 +0100 Subject: [PATCH 03/10] feat(docs): enable tags in docs --- docs/reference.md | 55 +++++++++++++++++++++++++++------- scripts/generate-cli-docs.ts | 2 ++ src/commands/builds/add-tag.ts | 2 +- 3 files changed, 48 insertions(+), 11 deletions(-) diff --git a/docs/reference.md b/docs/reference.md index d908a9f02..08541ef49 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -249,16 +249,19 @@ DESCRIPTION WARNING: Overwrites existing 'storage' directory. USAGE - $ apify init [actorName] [-y] + $ apify init [actorName] [--dockerfile ] [-y] ARGUMENTS actorName Name of the Actor. If not provided, you will be prompted for it. FLAGS - -y, --yes Automatic yes to prompts; assume "yes" as answer to all - prompts. Note that in some cases, the command may still ask for - confirmation. + --dockerfile= Path to a Dockerfile to use for + the Actor (e.g., "./Dockerfile" or + "./docker/Dockerfile"). + -y, --yes Automatic yes to prompts; + assume "yes" as answer to all prompts. Note that in some + cases, the command may still ask for confirmation. ``` ##### `apify run` @@ -726,12 +729,28 @@ DESCRIPTION Manages Actor build processes and versioning. SUBCOMMANDS - builds rm Permanently removes an Actor build from the Apify - platform. - builds ls Lists all builds of the Actor. - builds log Prints the log of a specific build. - builds info Prints information about a specific build. - builds create Creates a new build of the Actor. + builds add-tag Adds a tag to a specific Actor build. + builds remove-tag Removes a tag from a specific Actor build. + builds rm Permanently removes an Actor build from + the Apify platform. + builds ls Lists all builds of the Actor. + builds log Prints the log of a specific build. + builds info Prints information about a specific build. + builds create Creates a new build of the Actor. +``` + +##### `apify builds add-tag` + +```sh +DESCRIPTION + Adds a tag to a specific Actor build. + +USAGE + $ apify builds add-tag -b -t + +FLAGS + -b, --build= The build ID to tag. + -t, --tag= The tag to add to the build. ``` ##### `apify builds create` / `apify actors build` @@ -810,6 +829,22 @@ FLAGS --offset= Number of builds that will be skipped. ``` +##### `apify builds remove-tag` + +```sh +DESCRIPTION + Removes a tag from a specific Actor build. + +USAGE + $ apify builds remove-tag -b -t [-y] + +FLAGS + -b, --build= The build ID to remove the tag from. + -t, --tag= The tag to remove from the build. + -y, --yes Automatic yes to prompts; assume "yes" + as answer to all prompts. +``` + ##### `apify builds rm` ```sh diff --git a/scripts/generate-cli-docs.ts b/scripts/generate-cli-docs.ts index 4e3bf20f8..521c9cb5e 100644 --- a/scripts/generate-cli-docs.ts +++ b/scripts/generate-cli-docs.ts @@ -49,10 +49,12 @@ const categories: Record = { 'actor-build': [ // { command: Commands.builds }, + { command: Commands.buildsAddTag }, { command: Commands.buildsCreate, aliases: [Commands.actorsBuild] }, { command: Commands.buildsInfo }, { command: Commands.buildsLog }, { command: Commands.buildsLs }, + { command: Commands.buildsRemoveTag }, { command: Commands.buildsRm }, ], 'actor-run': [ diff --git a/src/commands/builds/add-tag.ts b/src/commands/builds/add-tag.ts index be4ac0904..1472f97c4 100644 --- a/src/commands/builds/add-tag.ts +++ b/src/commands/builds/add-tag.ts @@ -71,7 +71,7 @@ export class BuildsAddTagCommand extends ApifyCommand Date: Sun, 25 Jan 2026 11:42:56 +0100 Subject: [PATCH 04/10] chore(test): fix test --- test/api/commands/builds/tags.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/api/commands/builds/tags.test.ts b/test/api/commands/builds/tags.test.ts index bb9a3afd6..9f9b1e797 100644 --- a/test/api/commands/builds/tags.test.ts +++ b/test/api/commands/builds/tags.test.ts @@ -31,7 +31,7 @@ describe('[api] apify builds add-tag / remove-tag', () => { versions: [ { versionNumber: '0.0', - sourceType: 'SOURCE_FILES', + sourceType: 'SOURCE_FILES' as any, buildTag: 'latest', sourceFiles: [ { @@ -87,7 +87,7 @@ describe('[api] apify builds add-tag / remove-tag', () => { flags_tag: 'existing-tag', }); - expect(lastErrorMessage()).toMatch(/already tagged/i); + expect(lastLogMessage()).toMatch(/already tagged/i); }); it('should show error when build does not exist', async () => { From de3d71d876c133afb72d1a0219c4d44415175d87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20Sol=C3=A1r?= Date: Tue, 27 Jan 2026 11:25:40 +0100 Subject: [PATCH 05/10] refactor(tests): refactor test assertions --- test/api/commands/builds/tags.test.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/test/api/commands/builds/tags.test.ts b/test/api/commands/builds/tags.test.ts index 9f9b1e797..34d61c5cd 100644 --- a/test/api/commands/builds/tags.test.ts +++ b/test/api/commands/builds/tags.test.ts @@ -14,7 +14,7 @@ const ACTOR_NAME = useUniqueId('cli-builds-tag-test'); useAuthSetup({ perTest: false }); -const { lastErrorMessage, lastLogMessage } = useConsoleSpy(); +const { lastLogMessage } = useConsoleSpy(); const TEST_TIMEOUT = 120_000; @@ -67,7 +67,7 @@ describe('[api] apify builds add-tag / remove-tag', () => { flags_tag: 'beta', }); - expect(lastLogMessage()).toMatch(/tag.*beta.*added/i); + expect(lastLogMessage().toLowerCase()).toContain('tag "beta" added'); // Verify via API const actor = await testUserClient.actor(testActor.id).get(); @@ -87,7 +87,7 @@ describe('[api] apify builds add-tag / remove-tag', () => { flags_tag: 'existing-tag', }); - expect(lastLogMessage()).toMatch(/already tagged/i); + expect(lastLogMessage()).include('already tagged'); }); it('should show error when build does not exist', async () => { @@ -96,10 +96,10 @@ describe('[api] apify builds add-tag / remove-tag', () => { flags_tag: 'test-tag', }); - expect(lastErrorMessage()).toMatch(/not found/i); + expect(lastLogMessage()).include('not found'); }); - it( + it.only( 'should show previous build info when reassigning a tag', async () => { // Tag the build with 'reassign-test' @@ -118,7 +118,7 @@ describe('[api] apify builds add-tag / remove-tag', () => { flags_tag: 'reassign-test', }); - expect(lastLogMessage()).toMatch(/previously pointed to build/i); + expect(lastLogMessage()).include('previously pointed to build'); // Verify via API const actor = await testUserClient.actor(testActor.id).get(); @@ -147,7 +147,7 @@ describe('[api] apify builds add-tag / remove-tag', () => { flags_yes: true, }); - expect(lastLogMessage()).toMatch(/tag.*to-remove.*removed/i); + expect(lastLogMessage()).include('tag "to-remove" removed'); // Verify via API actor = await testUserClient.actor(testActor.id).get(); @@ -161,7 +161,7 @@ describe('[api] apify builds add-tag / remove-tag', () => { flags_yes: true, }); - expect(lastErrorMessage()).toMatch(/does not exist/i); + expect(lastLogMessage()).include('does not exist'); }); it( @@ -183,7 +183,7 @@ describe('[api] apify builds add-tag / remove-tag', () => { flags_yes: true, }); - expect(lastErrorMessage()).toMatch(/not associated with build/i); + expect(lastLogMessage()).include('not associated with build'); }, TEST_TIMEOUT, ); @@ -195,7 +195,7 @@ describe('[api] apify builds add-tag / remove-tag', () => { flags_yes: true, }); - expect(lastErrorMessage()).toMatch(/not found/i); + expect(lastLogMessage()).include('not found'); }); }); }); From 5711bc2944bd2ffccfb53d16cbeadd1d51ec0c56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20Sol=C3=A1r?= Date: Tue, 27 Jan 2026 12:29:47 +0100 Subject: [PATCH 06/10] Refactor(test): remove .only --- test/api/commands/builds/tags.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/api/commands/builds/tags.test.ts b/test/api/commands/builds/tags.test.ts index 34d61c5cd..7ef7c85a9 100644 --- a/test/api/commands/builds/tags.test.ts +++ b/test/api/commands/builds/tags.test.ts @@ -99,7 +99,7 @@ describe('[api] apify builds add-tag / remove-tag', () => { expect(lastLogMessage()).include('not found'); }); - it.only( + it( 'should show previous build info when reassigning a tag', async () => { // Tag the build with 'reassign-test' From 530cf0e90eb18538099cece84653713165680a2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20Sol=C3=A1r?= Date: Tue, 27 Jan 2026 14:58:37 +0100 Subject: [PATCH 07/10] refactor: fix failing test --- test/api/commands/builds/tags.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/api/commands/builds/tags.test.ts b/test/api/commands/builds/tags.test.ts index 7ef7c85a9..709bc6c67 100644 --- a/test/api/commands/builds/tags.test.ts +++ b/test/api/commands/builds/tags.test.ts @@ -147,7 +147,7 @@ describe('[api] apify builds add-tag / remove-tag', () => { flags_yes: true, }); - expect(lastLogMessage()).include('tag "to-remove" removed'); + expect(lastLogMessage()).include('Tag "to-remove" removed'); // Verify via API actor = await testUserClient.actor(testActor.id).get(); From 3e18aab89566a0aebe01bf8a9977819fa230be58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20Sol=C3=A1r?= Date: Tue, 27 Jan 2026 15:56:59 +0100 Subject: [PATCH 08/10] chore: updates apify-client to v2.22.0 --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index ad4acdebb..1a3de5fab 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "@skyra/jaro-winkler": "^1.1.1", "adm-zip": "~0.5.15", "ajv": "~8.17.1", - "apify-client": "2.21.1-beta.11", + "apify-client": "2.22.0", "archiver": "~7.0.1", "axios": "^1.11.0", "chalk": "~5.6.0", diff --git a/yarn.lock b/yarn.lock index 22c45d54d..f49ded337 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2300,7 +2300,7 @@ __metadata: adm-zip: "npm:~0.5.15" ajv: "npm:~8.17.1" apify: "npm:^3.2.4" - apify-client: "npm:2.21.1-beta.11" + apify-client: "npm:2.22.0" archiver: "npm:~7.0.1" axios: "npm:^1.11.0" chalk: "npm:~5.6.0" @@ -2348,9 +2348,9 @@ __metadata: languageName: unknown linkType: soft -"apify-client@npm:2.21.1-beta.11": - version: 2.21.1-beta.11 - resolution: "apify-client@npm:2.21.1-beta.11" +"apify-client@npm:2.22.0": + version: 2.22.0 + resolution: "apify-client@npm:2.22.0" dependencies: "@apify/consts": "npm:^2.42.0" "@apify/log": "npm:^2.2.6" @@ -2364,7 +2364,7 @@ __metadata: proxy-agent: "npm:^6.5.0" tslib: "npm:^2.5.0" type-fest: "npm:^4.0.0" - checksum: 10c0/60177986b424fc98eb7c3487f8f7958c1b5d7419deea8ea3852dd57ef971573a91f9f541f8c6cc4771a137376f110669f290ec763e0bdac06167de9a16213883 + checksum: 10c0/9fb6f22665c7dd8ea19b2e92a7ce9ee3bdcd9af200acb251d6ca1ddd4d6bdcba532d7934da540a6f2447df633590f8a03c4312e1393460c9bf5219b71177cede languageName: node linkType: hard From a9dd9ec09a22c17f2fa8375d121079c04cac13d5 Mon Sep 17 00:00:00 2001 From: Vlad Frangu Date: Tue, 27 Jan 2026 17:06:17 +0200 Subject: [PATCH 09/10] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1a3de5fab..b87e60ee3 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "@skyra/jaro-winkler": "^1.1.1", "adm-zip": "~0.5.15", "ajv": "~8.17.1", - "apify-client": "2.22.0", + "apify-client": "~2.22.0", "archiver": "~7.0.1", "axios": "^1.11.0", "chalk": "~5.6.0", From 1090773aa48a5e662dbfd5762a8dd3b310cfe198 Mon Sep 17 00:00:00 2001 From: Vlad Frangu Date: Tue, 27 Jan 2026 17:07:03 +0200 Subject: [PATCH 10/10] chore: yarn lock --- yarn.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/yarn.lock b/yarn.lock index f49ded337..6b6023b48 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2300,7 +2300,7 @@ __metadata: adm-zip: "npm:~0.5.15" ajv: "npm:~8.17.1" apify: "npm:^3.2.4" - apify-client: "npm:2.22.0" + apify-client: "npm:~2.22.0" archiver: "npm:~7.0.1" axios: "npm:^1.11.0" chalk: "npm:~5.6.0" @@ -2348,9 +2348,9 @@ __metadata: languageName: unknown linkType: soft -"apify-client@npm:2.22.0": - version: 2.22.0 - resolution: "apify-client@npm:2.22.0" +"apify-client@npm:^2.17.0": + version: 2.21.0 + resolution: "apify-client@npm:2.21.0" dependencies: "@apify/consts": "npm:^2.42.0" "@apify/log": "npm:^2.2.6" @@ -2364,13 +2364,13 @@ __metadata: proxy-agent: "npm:^6.5.0" tslib: "npm:^2.5.0" type-fest: "npm:^4.0.0" - checksum: 10c0/9fb6f22665c7dd8ea19b2e92a7ce9ee3bdcd9af200acb251d6ca1ddd4d6bdcba532d7934da540a6f2447df633590f8a03c4312e1393460c9bf5219b71177cede + checksum: 10c0/12c169b777af9e4e7300ba5049be9a2e37fc6395680b926ad1eb6db677f6e599f9003c95510ca9ba9ce36e10de02359d8b8539721cd96712cac0827f3a4b965b languageName: node linkType: hard -"apify-client@npm:^2.17.0": - version: 2.21.0 - resolution: "apify-client@npm:2.21.0" +"apify-client@npm:~2.22.0": + version: 2.22.0 + resolution: "apify-client@npm:2.22.0" dependencies: "@apify/consts": "npm:^2.42.0" "@apify/log": "npm:^2.2.6" @@ -2384,7 +2384,7 @@ __metadata: proxy-agent: "npm:^6.5.0" tslib: "npm:^2.5.0" type-fest: "npm:^4.0.0" - checksum: 10c0/12c169b777af9e4e7300ba5049be9a2e37fc6395680b926ad1eb6db677f6e599f9003c95510ca9ba9ce36e10de02359d8b8539721cd96712cac0827f3a4b965b + checksum: 10c0/9fb6f22665c7dd8ea19b2e92a7ce9ee3bdcd9af200acb251d6ca1ddd4d6bdcba532d7934da540a6f2447df633590f8a03c4312e1393460c9bf5219b71177cede languageName: node linkType: hard