Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Code samples usage #2438

Open
wants to merge 104 commits into
base: master
Choose a base branch
from
Open

Code samples usage #2438

wants to merge 104 commits into from

Conversation

adriendupuis
Copy link
Contributor

@adriendupuis adriendupuis commented Jul 15, 2024

Question Answer
JIRA Ticket N/A
Versions master, 4.6
Edition N/A

Tool to search and preview code samples usage.
It outputs a list of Markdown code blocks using include_file, each followed by a preview of its rendering.
For examples:

  • php tools/code_samples/code_samples_usage.php code_samples/api/product_catalog/src/Command/AttributeCommand.php code_samples/api/product_catalog/src/Command/ProductCommand.php lists usages of the two files passed as arguments (argument count is unlimited).
  • php tools/code_samples/code_samples_usage.php lists every include_file usage.

indicates a highlighted line (hl_lines).

The tool is used by a GitHub action for CI on pull request. It compares the usage of modified code_samples/ files between the target branch and the current branch. If there is changes in code_samples/, this action posts its report as a comment on the PR. It deletes its previous comment if it exists. (It could have edit and update the previous one but an old comment can become hidden by default and stay hidden even if updated.) Because of GitHub limitation, the comment can be hard to read. A colorized version is available as HTML file in a downloadable archive (due to artefact limitation, it can't be seen online or downloaded unzipped).

See the "code_samples/ change report" comment below for an example with two bonus modification.

Bonus:

Two PHP files are modified for demo, they had too long __construct lines and new lines are added:

  • code_samples/api/product_catalog/src/Command/AttributeCommand.php
  • code_samples/api/product_catalog/src/Command/ProductCommand.php

A report comment helps to check side effects on their usage.

The page using them doesn't change:

docs/pim/price_api.md has been modified. As the PHP code doesn't change, there is nothing about it in the report comment. Locally, the changes can be checked with php tools/code_samples/code_samples_usage.php code_samples/api/product_catalog/src/Command/ProductPriceCommand.php which could be simpler than running mkdocs server.

Remove duplicated blank lines before // ...:

Cases checks

  • There is no change in code_samples/
  • A code_samples/ file have been modified and its inclusions have different outputs than previously
  • A code_samples/ file have been modified but it makes no difference in its inclusions
  • A file as been renamed in code_samples/ but not in its inclusion
  • A file as been renamed in code_samples/ and in its inclusion
  • A file as been removed from code_samples/ but it's still included

Checklist

  • Text renders correctly
  • Text has been checked with vale
  • Description metadata is up to date
  • Redirects cover removed/moved pages
  • Code samples are working
  • PHP code samples have been fixed with PHP CS fixer
  • Added link to this PR in relevant JIRA ticket or code PR

@ibexa ibexa deleted a comment from github-actions bot Jul 15, 2024
@adriendupuis adriendupuis marked this pull request as ready for review July 29, 2024 15:03
@adriendupuis adriendupuis changed the title Code sample usage Code samples usage Jul 29, 2024
Copy link
Contributor

@mnocon mnocon left a comment

Choose a reason for hiding this comment

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

Thank you, this is much needed!

I'm just paranoid and I'm wondering if we can harden this a bit, just to be sure - but I really like this.

tools/code_samples/code_samples_usage.php Outdated Show resolved Hide resolved
{
$pattern = null === $codeSampleFilePath ? '= include_file' : $codeSampleFilePath;
$command = "grep '$pattern' -Rl docs | sort";
exec($command, $rawIncludingFileList, $commandResultCode);
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure there is an issue here - but just to be sure that there's no chance of command injection with some crazy filenames - could you use the Process component (https://symfony.com/doc/current/components/process.html)?

I'd also limit the scope of the token to the minimum (https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/controlling-permissions-for-github_token#defining-access-for-the-github_token-permissions) for this job, just for safety

Copy link
Contributor Author

@adriendupuis adriendupuis Sep 2, 2024

Choose a reason for hiding this comment

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

Command injection

I'd like to stay simple and not have to wrap all this into a Symfony application.

I'm using escapeshellarg around the file path since ebc14f5.

I tried args like php tools/code_samples/code_samples_usage.php "code_samples/api/product_catalog/src/Command/AttributeCommand.php'; echo TEST; :" to see if I could pass something to the exec. I didn't manage to. But hackers are imaginative.

Job permissions

I restricted permissions to their minimum:

        permissions:
            # Needed to manage the comment
            pull-requests: write

@adriendupuis
Copy link
Contributor Author

Another test in #2485

Copy link

github-actions bot commented Sep 5, 2024

code_samples/ change report

Before (on target branch)After (in current PR)

code_samples/api/product_catalog/src/Command/AttributeCommand.php

docs/pim/product_api.md@147:``` php

code_samples/api/product_catalog/src/Command/AttributeCommand.php

docs/pim/product_api.md@147:``` php
docs/pim/product_api.md@148:[[= include_file('code_samples/api/product_catalog/src/Command/AttributeCommand.php', 64, 65) =]]
docs/pim/product_api.md@149:[[= include_file('code_samples/api/product_catalog/src/Command/AttributeCommand.php', 85, 90) =]]
docs/pim/product_api.md@148:[[= include_file('code_samples/api/product_catalog/src/Command/AttributeCommand.php', 71, 72) =]]
docs/pim/product_api.md@149:[[= include_file('code_samples/api/product_catalog/src/Command/AttributeCommand.php', 92, 97) =]]
docs/pim/product_api.md@150:```

001⫶ $attributeGroup = $this->attributeGroupService->getAttributeGroup('dimensions');
002⫶
003⫶ $attributeGroups = $this->attributeGroupService->findAttributeGroups();
004⫶
005⫶ foreach ($attributeGroups as $attributeGroup) {
006⫶ $output->writeln('Attribute group ' . $attributeGroup->getIdentifier() . ' with name ' . $attributeGroup->getName());
007⫶ }

docs/pim/product_api.md@155:``` php
docs/pim/product_api.md@150:```

001⫶ $attributeGroup = $this->attributeGroupService->getAttributeGroup('dimensions');
002⫶
003⫶ $attributeGroups = $this->attributeGroupService->findAttributeGroups();
004⫶
005⫶ foreach ($attributeGroups as $attributeGroup) {
006⫶ $output->writeln('Attribute group ' . $attributeGroup->getIdentifier() . ' with name ' . $attributeGroup->getName());
007⫶ }

docs/pim/product_api.md@155:``` php
docs/pim/product_api.md@156:[[= include_file('code_samples/api/product_catalog/src/Command/AttributeCommand.php', 59, 63) =]]
docs/pim/product_api.md@156:[[= include_file('code_samples/api/product_catalog/src/Command/AttributeCommand.php', 66, 70) =]]
docs/pim/product_api.md@157:```

001⫶ $attributeGroupCreateStruct = $this->localAttributeGroupService->newAttributeGroupCreateStruct('dimensions');
002⫶ $attributeGroupCreateStruct->setNames(['eng-GB' => 'Size']);
003⫶
004⫶ $this->localAttributeGroupService->createAttributeGroup($attributeGroupCreateStruct);

docs/pim/product_api.md@162:``` php
docs/pim/product_api.md@157:```

001⫶ $attributeGroupCreateStruct = $this->localAttributeGroupService->newAttributeGroupCreateStruct('dimensions');
002⫶ $attributeGroupCreateStruct->setNames(['eng-GB' => 'Size']);
003⫶
004⫶ $this->localAttributeGroupService->createAttributeGroup($attributeGroupCreateStruct);

docs/pim/product_api.md@162:``` php
docs/pim/product_api.md@163:[[= include_file('code_samples/api/product_catalog/src/Command/AttributeCommand.php', 71, 73) =]]
docs/pim/product_api.md@163:[[= include_file('code_samples/api/product_catalog/src/Command/AttributeCommand.php', 78, 80) =]]
docs/pim/product_api.md@164:```

001⫶ $attribute = $this->attributeDefinitionService->getAttributeDefinition('length');
002⫶ $output->writeln($attribute->getName());

docs/pim/product_api.md@169:``` php
docs/pim/product_api.md@164:```

001⫶ $attribute = $this->attributeDefinitionService->getAttributeDefinition('length');
002⫶ $output->writeln($attribute->getName());

docs/pim/product_api.md@169:``` php
docs/pim/product_api.md@170:[[= include_file('code_samples/api/product_catalog/src/Command/AttributeCommand.php', 76, 82) =]]
docs/pim/product_api.md@170:[[= include_file('code_samples/api/product_catalog/src/Command/AttributeCommand.php', 83, 89) =]]
docs/pim/product_api.md@171:```

001⫶ $attributeCreateStruct = $this->localAttributeDefinitionService->newAttributeDefinitionCreateStruct('size');
002⫶ $attributeCreateStruct->setType($attributeType);
003⫶ $attributeCreateStruct->setName('eng-GB', 'Size');
004⫶ $attributeCreateStruct->setGroup($attributeGroup);
005⫶
006⫶ $this->localAttributeDefinitionService->createAttributeDefinition($attributeCreateStruct);


code_samples/api/product_catalog/src/Command/ProductCommand.php

docs/pim/product_api.md@22:``` php
docs/pim/product_api.md@171:```

001⫶ $attributeCreateStruct = $this->localAttributeDefinitionService->newAttributeDefinitionCreateStruct('size');
002⫶ $attributeCreateStruct->setType($attributeType);
003⫶ $attributeCreateStruct->setName('eng-GB', 'Size');
004⫶ $attributeCreateStruct->setGroup($attributeGroup);
005⫶
006⫶ $this->localAttributeDefinitionService->createAttributeDefinition($attributeCreateStruct);


code_samples/api/product_catalog/src/Command/ProductCommand.php

docs/pim/product_api.md@22:``` php
docs/pim/product_api.md@23:[[= include_file('code_samples/api/product_catalog/src/Command/ProductCommand.php', 62, 65) =]]
docs/pim/product_api.md@23:[[= include_file('code_samples/api/product_catalog/src/Command/ProductCommand.php', 68, 71) =]]
docs/pim/product_api.md@24:```

001⫶ $product = $this->productService->getProduct($productCode);
002⫶
003⫶ $output->writeln('Product with code ' . $product->getCode() . ' is ' . $product->getName());

docs/pim/product_api.md@29:``` php
docs/pim/product_api.md@24:```

001⫶ $product = $this->productService->getProduct($productCode);
002⫶
003⫶ $output->writeln('Product with code ' . $product->getCode() . ' is ' . $product->getName());

docs/pim/product_api.md@29:``` php
docs/pim/product_api.md@30:[[= include_file('code_samples/api/product_catalog/src/Command/ProductCommand.php', 66, 76) =]]
docs/pim/product_api.md@30:[[= include_file('code_samples/api/product_catalog/src/Command/ProductCommand.php', 72, 82) =]]
docs/pim/product_api.md@31:```

001⫶ $criteria = new Criterion\ProductType([$productType]);
002⫶ $sortClauses = [new SortClause\ProductName(ProductQuery::SORT_ASC)];
003⫶
004⫶ $productQuery = new ProductQuery(null, $criteria, $sortClauses);
005⫶
006⫶ $products = $this->productService->findProducts($productQuery);
007⫶
008⫶ foreach ($products as $product) {
009⫶ $output->writeln($product->getName() . ' of type ' . $product->getProductType()->getName());
010⫶ }

docs/pim/product_api.md@37:``` php
docs/pim/product_api.md@31:```

001⫶ $criteria = new Criterion\ProductType([$productType]);
002⫶ $sortClauses = [new SortClause\ProductName(ProductQuery::SORT_ASC)];
003⫶
004⫶ $productQuery = new ProductQuery(null, $criteria, $sortClauses);
005⫶
006⫶ $products = $this->productService->findProducts($productQuery);
007⫶
008⫶ foreach ($products as $product) {
009⫶ $output->writeln($product->getName() . ' of type ' . $product->getProductType()->getName());
010⫶ }

docs/pim/product_api.md@37:``` php
docs/pim/product_api.md@38:[[= include_file('code_samples/api/product_catalog/src/Command/ProductCommand.php', 87, 91) =]]
docs/pim/product_api.md@38:[[= include_file('code_samples/api/product_catalog/src/Command/ProductCommand.php', 93, 97) =]]
docs/pim/product_api.md@39:```

001⫶ $productUpdateStruct = $this->localProductService->newProductUpdateStruct($product);
002⫶ $productUpdateStruct->setCode('NEWMODIFIEDPRODUCT');
003⫶
004⫶ $this->localProductService->updateProduct($productUpdateStruct);

docs/pim/product_api.md@45:``` php
docs/pim/product_api.md@39:```

001⫶ $productUpdateStruct = $this->localProductService->newProductUpdateStruct($product);
002⫶ $productUpdateStruct->setCode('NEWMODIFIEDPRODUCT');
003⫶
004⫶ $this->localProductService->updateProduct($productUpdateStruct);

docs/pim/product_api.md@45:``` php
docs/pim/product_api.md@46:[[= include_file('code_samples/api/product_catalog/src/Command/ProductCommand.php', 77, 84) =]]
docs/pim/product_api.md@46:[[= include_file('code_samples/api/product_catalog/src/Command/ProductCommand.php', 83, 90) =]]
docs/pim/product_api.md@47:```

001⫶ $productType = $this->productTypeService->getProductType($productType);
002⫶
003⫶ $createStruct = $this->localProductService->newProductCreateStruct($productType, 'eng-GB');
004⫶ $createStruct->setCode('NEWPRODUCT');
005⫶ $createStruct->setField('name', 'New Product');
006⫶
007⫶ $this->localProductService->createProduct($createStruct);

docs/pim/product_api.md@51:``` php
docs/pim/product_api.md@47:```

001⫶ $productType = $this->productTypeService->getProductType($productType);
002⫶
003⫶ $createStruct = $this->localProductService->newProductCreateStruct($productType, 'eng-GB');
004⫶ $createStruct->setCode('NEWPRODUCT');
005⫶ $createStruct->setField('name', 'New Product');
006⫶
007⫶ $this->localProductService->createProduct($createStruct);

docs/pim/product_api.md@51:``` php
docs/pim/product_api.md@52:[[= include_file('code_samples/api/product_catalog/src/Command/ProductCommand.php', 114, 115) =]]
docs/pim/product_api.md@52:[[= include_file('code_samples/api/product_catalog/src/Command/ProductCommand.php', 120, 121) =]]
docs/pim/product_api.md@53:```

001⫶ $this->localProductService->deleteProduct($product);

docs/pim/product_api.md@127:```php
docs/pim/product_api.md@53:```

001⫶ $this->localProductService->deleteProduct($product);

docs/pim/product_api.md@127:```php
docs/pim/product_api.md@128:[[= include_file('code_samples/api/product_catalog/src/Command/ProductCommand.php', 98, 103) =]]        }
docs/pim/product_api.md@128:[[= include_file('code_samples/api/product_catalog/src/Command/ProductCommand.php', 104, 109) =]]        }
docs/pim/product_api.md@129:```

001⫶ if ($this->productAvailabilityService->hasAvailability($product)) {
002⫶ $availability = $this->productAvailabilityService->getAvailability($product);
003⫶
004⫶ $output->write($availability->isAvailable() ? 'Available' : 'Unavailable');
005⫶ $output->writeln(' with stock ' . $availability->getStock());
006⫶ }

docs/pim/product_api.md@135:``` php
docs/pim/product_api.md@129:```

001⫶ if ($this->productAvailabilityService->hasAvailability($product)) {
002⫶ $availability = $this->productAvailabilityService->getAvailability($product);
003⫶
004⫶ $output->write($availability->isAvailable() ? 'Available' : 'Unavailable');
005⫶ $output->writeln(' with stock ' . $availability->getStock());
006⫶ }

docs/pim/product_api.md@135:``` php
docs/pim/product_api.md@136:[[= include_file('code_samples/api/product_catalog/src/Command/ProductCommand.php', 106, 109) =]]
docs/pim/product_api.md@136:[[= include_file('code_samples/api/product_catalog/src/Command/ProductCommand.php', 112, 115) =]]
docs/pim/product_api.md@137:```

001⫶ $productAvailabilityUpdateStruct = new ProductAvailabilityUpdateStruct($product, true, false, 80);
002⫶
003⫶ $this->productAvailabilityService->updateProductAvailability($productAvailabilityUpdateStruct);

docs/pim/product_api.md@137:```

001⫶ $productAvailabilityUpdateStruct = new ProductAvailabilityUpdateStruct($product, true, false, 80);
002⫶
003⫶ $this->productAvailabilityService->updateProductAvailability($productAvailabilityUpdateStruct);

Download colorized diff

Copy link
Contributor

@mnocon mnocon left a comment

Choose a reason for hiding this comment

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

Thank you!

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

Successfully merging this pull request may close these issues.

2 participants