Skip to content

Commit

Permalink
[DEVSVCS-556] Workflow Registry Contract Fixes (#15923)
Browse files Browse the repository at this point in the history
* <URL Updates Allow Empty Strings> do not allow empty urls for binaryURL

* <redundant check on workflowID updated> since workflowID must be unique this already covers that the new one must also be different from the new one

* <Checks For Duplicates In Workflow Registry> do not add a new version to the registry manager; instead activate the existing one if desired

* <Register Name Allow Empty Strings> do not allow name to be empty

* minor gas optimization for updateAllowedDONs and updateAuthorizedAddresses

* gas optimization for update status: bundle multiple status condition checks into one

* run forge fmt + update gas snapshot + add changeset

* address pr comment: move check for contract existence to top of function call

* update core tests

* Update gethwrappers

---------

Co-authored-by: app-token-issuer-infra-releng[bot] <120227048+app-token-issuer-infra-releng[bot]@users.noreply.github.com>
  • Loading branch information
1 parent d44df01 commit 912d6bc
Show file tree
Hide file tree
Showing 13 changed files with 140 additions and 77 deletions.
5 changes: 5 additions & 0 deletions contracts/.changeset/ninety-pianos-approve.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@chainlink/contracts': patch
---

Address misc fixes for workflow registry contract
54 changes: 27 additions & 27 deletions contracts/gas-snapshots/workflow.gas-snapshot
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
WorkflowRegistryManager_activateVersion:test_WhenTheVersionNumberIsNotActive_AndWhenThereAreNoActiveVersions() (gas: 528769)
WorkflowRegistryManager_addVersion:test_WhenAutoActivateIsFalse() (gas: 265551)
WorkflowRegistryManager_addVersion:test_WhenAutoActivateIsTrue() (gas: 270596)
WorkflowRegistryManager_getActiveVersion:test_WhenAnActiveVersionExists() (gas: 287760)
WorkflowRegistryManager_activateVersion:test_WhenTheVersionNumberIsNotActive_AndWhenThereAreNoActiveVersions() (gas: 529151)
WorkflowRegistryManager_addVersion:test_WhenAutoActivateIsFalse() (gas: 265730)
WorkflowRegistryManager_addVersion:test_WhenAutoActivateIsTrue() (gas: 270806)
WorkflowRegistryManager_getActiveVersion:test_WhenAnActiveVersionExists() (gas: 287951)
WorkflowRegistryManager_getActiveVersion:test_WhenNoActiveVersionIsAvailable() (gas: 13258)
WorkflowRegistryManager_getActiveVersionNumber:test_WhenAnActiveVersionExists() (gas: 283885)
WorkflowRegistryManager_getActiveVersionNumber:test_WhenAnActiveVersionExists() (gas: 284076)
WorkflowRegistryManager_getActiveVersionNumber:test_WhenNoActiveVersionIsAvailable() (gas: 10698)
WorkflowRegistryManager_getAllVersions:test_WhenLimitExceedsMaximumPaginationLimit() (gas: 54503)
WorkflowRegistryManager_getAllVersions:test_WhenRequestingWithInvalidStartIndex() (gas: 11338)
WorkflowRegistryManager_getAllVersions:test_WhenRequestingWithValidStartIndexAndLimitWithinBounds() (gas: 40398)
WorkflowRegistryManager_getLatestVersion:test_WhenNoVersionsHaveBeenRegistered() (gas: 12984)
WorkflowRegistryManager_getLatestVersion:test_WhenVersionsHaveBeenRegistered() (gas: 287791)
WorkflowRegistryManager_getLatestVersion:test_WhenVersionsHaveBeenRegistered() (gas: 287982)
WorkflowRegistryManager_getLatestVersionNumber:test_WhenNoVersionsHaveBeenRegistered() (gas: 10637)
WorkflowRegistryManager_getLatestVersionNumber:test_WhenVersionsHaveBeenRegistered() (gas: 284134)
WorkflowRegistryManager_getLatestVersionNumber:test_WhenVersionsHaveBeenRegistered() (gas: 284325)
WorkflowRegistryManager_getVersion:test_WhenVersionNumberIsNotRegistered() (gas: 13785)
WorkflowRegistryManager_getVersion:test_WhenVersionNumberIsRegistered() (gas: 288169)
WorkflowRegistryManager_getVersionNumberByContractAddressAndChainID:test_WhenAVersionIsRegisteredForTheContractAddressAndChainIDCombination() (gas: 285022)
WorkflowRegistryManager_getVersionNumberByContractAddressAndChainID:test_WhenNoVersionIsRegisteredForTheContractAddressAndChainIDCombination() (gas: 286634)
WorkflowRegistryManager_getVersionNumberByContractAddressAndChainID:test_WhenTheContractAddressIsInvalid() (gas: 284604)
WorkflowRegistry_activateWorkflow:test_WhenTheCallerIsAnAuthorizedAddress() (gas: 517416)
WorkflowRegistry_deleteWorkflow:test_WhenTheCallerIsAnAuthorizedAddress_AndTheDonIDIsAllowed() (gas: 422157)
WorkflowRegistry_deleteWorkflow:test_WhenTheCallerIsAnAuthorizedAddress_AndTheDonIDIsNotAllowed() (gas: 439960)
WorkflowRegistryManager_getVersion:test_WhenVersionNumberIsRegistered() (gas: 288360)
WorkflowRegistryManager_getVersionNumberByContractAddressAndChainID:test_WhenAVersionIsRegisteredForTheContractAddressAndChainIDCombination() (gas: 285213)
WorkflowRegistryManager_getVersionNumberByContractAddressAndChainID:test_WhenNoVersionIsRegisteredForTheContractAddressAndChainIDCombination() (gas: 286825)
WorkflowRegistryManager_getVersionNumberByContractAddressAndChainID:test_WhenTheContractAddressIsInvalid() (gas: 284795)
WorkflowRegistry_activateWorkflow:test_WhenTheCallerIsAnAuthorizedAddress() (gas: 517448)
WorkflowRegistry_deleteWorkflow:test_WhenTheCallerIsAnAuthorizedAddress_AndTheDonIDIsAllowed() (gas: 422188)
WorkflowRegistry_deleteWorkflow:test_WhenTheCallerIsAnAuthorizedAddress_AndTheDonIDIsNotAllowed() (gas: 439990)
WorkflowRegistry_getAllAllowedDONs:test_WhenTheRegistryIsLocked() (gas: 47473)
WorkflowRegistry_getAllAllowedDONs:test_WhenTheSetOfAllowedDONsIsEmpty() (gas: 25780)
WorkflowRegistry_getAllAllowedDONs:test_WhenThereAreMultipleAllowedDONs() (gas: 75437)
Expand All @@ -28,9 +28,9 @@ WorkflowRegistry_getAllAuthorizedAddresses:test_WhenTheRegistryIsLocked() (gas:
WorkflowRegistry_getAllAuthorizedAddresses:test_WhenTheSetOfAuthorizedAddressesIsEmpty() (gas: 26152)
WorkflowRegistry_getAllAuthorizedAddresses:test_WhenThereAreMultipleAuthorizedAddresses() (gas: 78270)
WorkflowRegistry_getAllAuthorizedAddresses:test_WhenThereIsASingleAuthorizedAddress() (gas: 16832)
WorkflowRegistry_getWorkflowMetadata:test_WhenTheRegistryIsLocked() (gas: 541532)
WorkflowRegistry_getWorkflowMetadata:test_WhenTheRegistryIsLocked() (gas: 541570)
WorkflowRegistry_getWorkflowMetadata:test_WhenTheWorkflowDoesNotExist() (gas: 17543)
WorkflowRegistry_getWorkflowMetadata:test_WhenTheWorkflowExistsWithTheOwnerAndName() (gas: 512388)
WorkflowRegistry_getWorkflowMetadata:test_WhenTheWorkflowExistsWithTheOwnerAndName() (gas: 512426)
WorkflowRegistry_getWorkflowMetadataListByDON:test_WhenLimitExceedsTotalWorkflows() (gas: 128146)
WorkflowRegistry_getWorkflowMetadataListByDON:test_WhenLimitIsEqualToTotalWorkflows() (gas: 128035)
WorkflowRegistry_getWorkflowMetadataListByDON:test_WhenLimitIsLessThanTotalWorkflows() (gas: 90141)
Expand All @@ -48,17 +48,17 @@ WorkflowRegistry_getWorkflowMetadataListByOwner:test_WhenStartIsGreaterThanOrEqu
WorkflowRegistry_getWorkflowMetadataListByOwner:test_WhenTheOwnerHasNoWorkflows() (gas: 14006)
WorkflowRegistry_getWorkflowMetadataListByOwner:test_WhenTheRegistryIsLocked() (gas: 165968)
WorkflowRegistry_lockRegistry:test_WhenTheCallerIsTheContractOwner() (gas: 38758)
WorkflowRegistry_pauseWorkflow:test_WhenTheDonIDIsAllowed_AndTheCallerIsAnAuthorizedAddress() (gas: 517380)
WorkflowRegistry_pauseWorkflow:test_WhenTheDonIDIsAllowed_AndTheCallerIsAnUnauthorizedAddress() (gas: 525183)
WorkflowRegistry_pauseWorkflow:test_WhenTheDonIDIsNotAllowed_AndTheCallerIsAnAuthorizedAddress() (gas: 524942)
WorkflowRegistry_pauseWorkflow:test_WhenTheDonIDIsNotAllowed_AndTheCallerIsAnUnauthorizedAddress() (gas: 529353)
WorkflowRegistry_registerWorkflow:test_WhenTheWorkflowInputsAreAllValid() (gas: 572178)
WorkflowRegistry_requestForceUpdateSecrets:test_WhenTheCallerIsAnAuthorizedAddress_AndTheWorkflowIsInAnAllowedDON() (gas: 936016)
WorkflowRegistry_requestForceUpdateSecrets:test_WhenTheCallerIsAnAuthorizedAddress_AndTheWorkflowIsNotInAnAllowedDON() (gas: 510784)
WorkflowRegistry_requestForceUpdateSecrets:test_WhenTheCallerIsNotAnAuthorizedAddress() (gas: 509138)
WorkflowRegistry_pauseWorkflow:test_WhenTheDonIDIsAllowed_AndTheCallerIsAnAuthorizedAddress() (gas: 517427)
WorkflowRegistry_pauseWorkflow:test_WhenTheDonIDIsAllowed_AndTheCallerIsAnUnauthorizedAddress() (gas: 525230)
WorkflowRegistry_pauseWorkflow:test_WhenTheDonIDIsNotAllowed_AndTheCallerIsAnAuthorizedAddress() (gas: 524989)
WorkflowRegistry_pauseWorkflow:test_WhenTheDonIDIsNotAllowed_AndTheCallerIsAnUnauthorizedAddress() (gas: 529400)
WorkflowRegistry_registerWorkflow:test_WhenTheWorkflowInputsAreAllValid() (gas: 572239)
WorkflowRegistry_requestForceUpdateSecrets:test_WhenTheCallerIsAnAuthorizedAddress_AndTheWorkflowIsInAnAllowedDON() (gas: 936092)
WorkflowRegistry_requestForceUpdateSecrets:test_WhenTheCallerIsAnAuthorizedAddress_AndTheWorkflowIsNotInAnAllowedDON() (gas: 510822)
WorkflowRegistry_requestForceUpdateSecrets:test_WhenTheCallerIsNotAnAuthorizedAddress() (gas: 509176)
WorkflowRegistry_unlockRegistry:test_WhenTheCallerIsTheContractOwner() (gas: 30325)
WorkflowRegistry_updateAllowedDONs:test_WhenTheBoolInputIsFalse() (gas: 29739)
WorkflowRegistry_updateAllowedDONs:test_WhenTheBoolInputIsTrue() (gas: 170296)
WorkflowRegistry_updateAllowedDONs:test_WhenTheBoolInputIsTrue() (gas: 170256)
WorkflowRegistry_updateAuthorizedAddresses:test_WhenTheBoolInputIsFalse() (gas: 30278)
WorkflowRegistry_updateAuthorizedAddresses:test_WhenTheBoolInputIsTrue() (gas: 175515)
WorkflowRegistry_updateWorkflow:test_WhenTheWorkflowInputsAreAllValid() (gas: 515666)
WorkflowRegistry_updateAuthorizedAddresses:test_WhenTheBoolInputIsTrue() (gas: 175475)
WorkflowRegistry_updateWorkflow:test_WhenTheWorkflowInputsAreAllValid() (gas: 515414)
44 changes: 24 additions & 20 deletions contracts/src/v0.8/workflow/dev/WorkflowRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ contract WorkflowRegistry is Ownable2StepMsgSender, ITypeAndVersion {
event RegistryUnlockedV1(address unlockedBy);

error AddressNotAuthorized(address caller);
error BinaryURLRequired();
error CallerIsNotWorkflowOwner(address caller);
error DONNotAllowed(uint32 donID);
error InvalidWorkflowID();
Expand All @@ -99,7 +100,7 @@ contract WorkflowRegistry is Ownable2StepMsgSender, ITypeAndVersion {
error WorkflowContentNotUpdated();
error WorkflowDoesNotExist();
error WorkflowIDAlreadyExists();
error WorkflowIDNotUpdated();
error WorkflowNameRequired();
error WorkflowNameTooLong(uint256 providedLength, uint8 maxAllowedLength);

modifier registryNotLocked() {
Expand All @@ -119,10 +120,12 @@ contract WorkflowRegistry is Ownable2StepMsgSender, ITypeAndVersion {
/// @param allowed True if they should be added to the allowlist, false to remove them.
function updateAllowedDONs(uint32[] calldata donIDs, bool allowed) external onlyOwner registryNotLocked {
uint256 length = donIDs.length;
for (uint256 i = 0; i < length; ++i) {
if (allowed) {
if (allowed) {
for (uint256 i = 0; i < length; ++i) {
s_allowedDONs.add(donIDs[i]);
} else {
}
} else {
for (uint256 i = 0; i < length; ++i) {
s_allowedDONs.remove(donIDs[i]);
}
}
Expand All @@ -136,10 +139,12 @@ contract WorkflowRegistry is Ownable2StepMsgSender, ITypeAndVersion {
/// @param allowed True if they should be added to whitelist, false to remove them.
function updateAuthorizedAddresses(address[] calldata addresses, bool allowed) external onlyOwner registryNotLocked {
uint256 length = addresses.length;
for (uint256 i = 0; i < length; ++i) {
if (allowed) {
if (allowed) {
for (uint256 i = 0; i < length; ++i) {
s_authorizedAddresses.add(addresses[i]);
} else {
}
} else {
for (uint256 i = 0; i < length; ++i) {
s_authorizedAddresses.remove(addresses[i]);
}
}
Expand Down Expand Up @@ -286,11 +291,6 @@ contract WorkflowRegistry is Ownable2StepMsgSender, ITypeAndVersion {
// Store the old workflowID for event emission.
bytes32 currentWorkflowID = workflow.workflowID;

// Condition to revert: WorkflowID must change, and at least one URL must change
if (currentWorkflowID == newWorkflowID) {
revert WorkflowIDNotUpdated();
}

// Determine which URLs have changed
bool sameBinaryURL = Strings.equal(workflow.binaryURL, binaryURL);
bool sameConfigURL = Strings.equal(workflow.configURL, configURL);
Expand Down Expand Up @@ -489,20 +489,16 @@ contract WorkflowRegistry is Ownable2StepMsgSender, ITypeAndVersion {
revert WorkflowAlreadyInDesiredStatus();
}

// Check if the DON ID is allowed when activating a workflow
// Emit the appropriate event based on newStatus
if (newStatus == WorkflowStatus.ACTIVE) {
_validatePermissions(donID, msg.sender);
emit WorkflowActivatedV1(workflow.workflowID, msg.sender, donID, workflow.workflowName);
} else if (newStatus == WorkflowStatus.PAUSED) {
emit WorkflowPausedV1(workflow.workflowID, msg.sender, donID, workflow.workflowName);
}

// Update the workflow status
workflow.status = newStatus;

// Emit the appropriate event based on newStatus
if (newStatus == WorkflowStatus.PAUSED) {
emit WorkflowPausedV1(workflow.workflowID, msg.sender, donID, workflow.workflowName);
} else if (newStatus == WorkflowStatus.ACTIVE) {
emit WorkflowActivatedV1(workflow.workflowID, msg.sender, donID, workflow.workflowName);
}
}

/// @dev Internal function to retrieve a workflow from storage.
Expand Down Expand Up @@ -669,6 +665,10 @@ contract WorkflowRegistry is Ownable2StepMsgSender, ITypeAndVersion {
uint256 configURLLength,
uint256 secretsURLLength
) internal pure {
if (binaryURLLength == 0) {
revert BinaryURLRequired();
}

if (binaryURLLength > MAX_URL_LENGTH) {
revert URLTooLong(binaryURLLength, MAX_URL_LENGTH);
}
Expand All @@ -688,6 +688,10 @@ contract WorkflowRegistry is Ownable2StepMsgSender, ITypeAndVersion {
function _validateWorkflowName(
uint256 workflowNameLength
) internal pure {
if (workflowNameLength == 0) {
revert WorkflowNameRequired();
}

if (workflowNameLength > MAX_WORKFLOW_NAME_LENGTH) {
revert WorkflowNameTooLong(workflowNameLength, MAX_WORKFLOW_NAME_LENGTH);
}
Expand Down
10 changes: 8 additions & 2 deletions contracts/src/v0.8/workflow/dev/WorkflowRegistryManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,13 @@ contract WorkflowRegistryManager is Ownable2StepMsgSender, ITypeAndVersion {
uint32 private s_latestVersionNumber = 0;

// Errors
error ContractAlreadyRegistered(address contractAddress, uint64 chainID);
error InvalidContractAddress(address invalidAddress);
error InvalidContractType(address invalidAddress);
error NoActiveVersionAvailable();
error NoVersionsRegistered();
error VersionNotRegistered(uint32 versionNumber);
error VersionAlreadyActive(uint32 versionNumber);
error VersionNotRegistered(uint32 versionNumber);
// Events

event VersionAdded(address indexed contractAddress, uint64 chainID, uint32 deployedAt, uint32 version);
Expand All @@ -63,6 +64,12 @@ contract WorkflowRegistryManager is Ownable2StepMsgSender, ITypeAndVersion {
/// @param autoActivate A boolean indicating whether the new version should be activated immediately.
/// @custom:throws InvalidContractType if the provided contract address is zero or not a WorkflowRegistry.
function addVersion(address contractAddress, uint64 chainID, uint32 deployedAt, bool autoActivate) external onlyOwner {
// Check if the contract is already registered. If it is, you can just activate that existing version.
bytes32 key = keccak256(abi.encodePacked(contractAddress, chainID));
if (s_versionNumberByAddressAndChainID[key] != 0) {
revert ContractAlreadyRegistered(contractAddress, chainID);
}

string memory typeVer = _getTypeAndVersionForContract(contractAddress);
uint32 latestVersionNumber = ++s_latestVersionNumber;

Expand All @@ -74,7 +81,6 @@ contract WorkflowRegistryManager is Ownable2StepMsgSender, ITypeAndVersion {
});

// Store the version number associated with the hash of contract address and chainID
bytes32 key = keccak256(abi.encodePacked(contractAddress, chainID));
s_versionNumberByAddressAndChainID[key] = latestVersionNumber;

if (autoActivate) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,22 @@ contract WorkflowRegistry_registerWorkflow is WorkflowRegistrySetup {
);
}

// whenTheCallerIsAnAuthorizedAddress whenTheRegistryIsNotLocked whenTheDonIDIsAllowed
function test_RevertWhen_TheWorkflowNameIsEmpty() external {
vm.prank(s_authorizedAddress);

vm.expectRevert(WorkflowRegistry.WorkflowNameRequired.selector);
s_registry.registerWorkflow(
"",
s_validWorkflowID,
s_allowedDonID,
WorkflowRegistry.WorkflowStatus.ACTIVE,
s_validBinaryURL,
s_validConfigURL,
s_validSecretsURL
);
}

// whenTheCallerIsAnAuthorizedAddress whenTheRegistryIsNotLocked whenTheDonIDIsAllowed
function test_RevertWhen_TheWorkflowNameIsTooLong() external {
vm.prank(s_authorizedAddress);
Expand All @@ -74,6 +90,22 @@ contract WorkflowRegistry_registerWorkflow is WorkflowRegistrySetup {
);
}

// whenTheCallerIsAnAuthorizedAddress whenTheRegistryIsNotLocked whenTheDonIDIsAllowed
function test_RevertWhen_TheBinaryURLIsEmpty() external {
vm.prank(s_authorizedAddress);

vm.expectRevert(WorkflowRegistry.BinaryURLRequired.selector);
s_registry.registerWorkflow(
s_validWorkflowName,
s_validWorkflowID,
s_allowedDonID,
WorkflowRegistry.WorkflowStatus.ACTIVE,
"",
s_validConfigURL,
s_validSecretsURL
);
}

// whenTheCallerIsAnAuthorizedAddress whenTheRegistryIsNotLocked whenTheDonIDIsAllowed
function test_RevertWhen_TheBinaryURLIsTooLong() external {
vm.prank(s_authorizedAddress);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@ WorkflowRegistry.registerWorkflow
├── when the caller is not an authorized address
│ └── it should revert
└── when the caller is an authorized address
── when the registry is locked
── when the registry is locked
│ └── it should revert
└── when the registry is not locked
── when the donID is not allowed
── when the donID is not allowed
│ └── it should revert
└── when the donID is allowed
├── when the workflow name is empty
│ └── it should revert
├── when the workflow name is too long
│ └── it should revert
├── when the binaryURL is empty
│ └── it should revert
├── when the binaryURL is too long
│ └── it should revert
├── when the configURL is too long
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,29 +83,27 @@ contract WorkflowRegistry_updateWorkflow is WorkflowRegistrySetup {
}

// whenTheCallerIsAnAuthorizedAddress whenTheRegistryIsNotLocked whenTheDonIDIsAllowed whenTheCallerIsTheWorkflowOwner
function test_RevertWhen_TheNewWorkflowIDIsTheSameAsTheExistingWorkflowID() external {
function test_RevertWhen_NoneOfTheURLsAreUpdated() external {
// Register a workflow first
_registerValidWorkflow();

// Update the workflow now with the same workflow ID
// Update the workflow with no changes to any URLs
vm.prank(s_authorizedAddress);
vm.expectRevert(WorkflowRegistry.WorkflowIDNotUpdated.selector);
vm.expectRevert(WorkflowRegistry.WorkflowContentNotUpdated.selector);
s_registry.updateWorkflow(
s_validWorkflowKey, s_validWorkflowID, s_validBinaryURL, s_validConfigURL, s_newValidSecretsURL
s_validWorkflowKey, s_newValidWorkflowID, s_validBinaryURL, s_validConfigURL, s_validSecretsURL
);
}

// whenTheCallerIsAnAuthorizedAddress whenTheRegistryIsNotLocked whenTheDonIDIsAllowed whenTheCallerIsTheWorkflowOwner
function test_RevertWhen_NoneOfTheURLsAreUpdated() external {
function test_RevertWhen_TheBinaryURLIsEmpty() external {
// Register a workflow first
_registerValidWorkflow();

// Update the workflow with no changes to any URLs
// Update the workflow with a binary URL that is empty
vm.prank(s_authorizedAddress);
vm.expectRevert(WorkflowRegistry.WorkflowContentNotUpdated.selector);
s_registry.updateWorkflow(
s_validWorkflowKey, s_newValidWorkflowID, s_validBinaryURL, s_validConfigURL, s_validSecretsURL
);
vm.expectRevert(WorkflowRegistry.BinaryURLRequired.selector);
s_registry.updateWorkflow(s_validWorkflowKey, s_newValidWorkflowID, "", s_validConfigURL, s_validSecretsURL);
}

// whenTheCallerIsAnAuthorizedAddress whenTheRegistryIsNotLocked whenTheDonIDIsAllowed whenTheCallerIsTheWorkflowOwner
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ WorkflowRegistry.updateWorkflow
└── when the caller is the workflow owner
├── when an existing workflow is not found with the given workflow name
│ └── it should revert
├── when the new workflowID is the same as the existing workflowID
│ └── it should revert
├── when none of the URLs are updated
│ └── it should revert
├── when the binaryURL is empty
│ └── it should revert
├── when the binaryURL is too long
│ └── it should revert
├── when the configURL is too long
Expand Down
Loading

0 comments on commit 912d6bc

Please sign in to comment.