diff --git a/README.md b/README.md index ced4fbb..b048947 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,38 @@ It's not mandatory to follow this recommendation, but it does help make it easier on maintainers. They'll be able to click the "Merge" button and the changes will be deployed immediately. +Bundles +------- + +The Bundles list on the home page and the community page is rendered +from `app/config/bundles.yml`. + +`scripts/fetch-bundles.php` discovers bundles on GitHub and refreshes +their metadata from GitHub and Packagist: + + GITHUB_TOKEN=... composer fetch-bundles + +A repository is discovered if it has the `sculpin-bundle` topic, or if +its name contains both "sculpin" and "bundle". The token is optional, +but without one the GitHub Search API allows only 10 requests per +minute. + +The script never publishes anything on its own. New finds are written to +the `discovered` list, which is not rendered; a maintainer moves the +entries that belong on the site into `bundles`, and adds anything that +should never be listed to `ignored`. + +To add a bundle by hand, add an entry to `bundles` with a `name`, +`repository` and `description`, then run the script to fill in the rest. +It refreshes `description`, `version`, `updated`, `stars`, `archived` +and `abandoned` on every run, keeps both lists in alphabetical order, and +leaves everything else alone. + +What gets rendered is decided in +`source/_views/includes/bundles.html`, not by the file order: bundles +marked abandoned on Packagist are skipped, and the rest are listed most +recently updated first. + Notes ----- diff --git a/app/config/bundles.yml b/app/config/bundles.yml new file mode 100644 index 0000000..3d0e85b --- /dev/null +++ b/app/config/bundles.yml @@ -0,0 +1,384 @@ +# Sculpin bundles listed on sculpin.io. +# +# `bundles` is rendered on the home page and the community page. `discovered` +# holds repositories found by scripts/fetch-bundles.php that a maintainer has +# not reviewed yet, and is never rendered. `ignored` repositories are skipped +# by discovery entirely. +# +# To add a bundle by hand, add an entry to `bundles` with at least `name`, +# `repository` and `description`, then run: +# +# composer fetch-bundles +# +# That run fills in the rest. It refreshes description, version, updated, +# stars, archived and abandoned on every run; `name` and any other keys you add +# are left alone. +# +# Both lists are kept in alphabetical order here. The site renders `bundles` +# most recently updated first, and skips anything marked abandoned; see +# source/_views/includes/bundles.html. + +bundles: + - + name: 'Icelus Thumbnail Generator' + package: beryllium/icelus + repository: 'https://github.com/beryllium/icelus' + description: 'Generate image thumbnails from within Twig.' + version: 2.2.0 + updated: '2026-07-07' + stars: 10 + archived: false + abandoned: false + - + name: 'Meta Navigation Bundle' + package: janbuecker/sculpin-meta-navigation-bundle + repository: 'https://github.com/janbuecker/sculpin-meta-navigation-bundle' + description: 'Build navigation menus from page front matter, with up to three levels of depth.' + version: v0.9 + updated: '2022-08-26' + stars: 0 + archived: false + abandoned: false + - + name: 'Related Posts Bundle' + package: tsphethean/sculpin-related-posts-bundle + repository: 'https://github.com/tsphethean/sculpin-related-posts-bundle' + description: 'List related posts based on tag and category correlations.' + version: 0.1.0 + updated: '2015-07-22' + stars: 5 + archived: false + abandoned: false + - + name: 'Sort By Field Bundle' + package: opdavies/sculpin-twig-sort-by-field-bundle + repository: 'https://github.com/opdavies/sculpin-twig-sort-by-field-bundle' + description: 'Sort arrays by key from within Twig.' + version: 1.0.0 + updated: '2017-03-05' + stars: 0 + archived: false + abandoned: false + - + name: 'Twig Markdown Bundle' + package: opdavies/sculpin-twig-markdown-bundle + repository: 'https://github.com/opdavies/sculpin-twig-markdown-bundle' + description: 'Render Markdown strings from within Twig.' + version: 0.2.0 + updated: '2019-12-28' + stars: 0 + archived: false + abandoned: false +discovered: + - + name: 'Ab Test Bundle' + package: yaenergetik/sculpin-ab-test-bundle + repository: 'https://github.com/Andrey96/sculpin-ab-test-bundle' + description: 'ABTest Bundle for Sculpin' + version: null + updated: '2016-08-18' + stars: 0 + archived: false + abandoned: false + - + name: 'Ab Test Bundle' + package: yaenergetik/sculpin-ab-test-bundle + repository: 'https://github.com/ertrade/sculpin-ab-test-bundle' + description: 'ABTest Bundle for Sculpin' + version: null + updated: '2016-08-19' + stars: 0 + archived: false + abandoned: false + - + name: 'Algolia Search Bundle' + package: timbroder/sculpin-algolia + repository: 'https://github.com/timbroder/SculpinAlgoliaSearchBundle' + description: 'Provides search in static site via Algolia https://www.algolia.com' + version: null + updated: '2016-01-05' + stars: 2 + archived: false + abandoned: false + - + name: 'Calendarian Bundle' + package: sharkpp/sculpin-calendarian-bundle + repository: 'https://github.com/sharkpp/sculpin-calendarian-bundle' + description: 'This is a Bundle that generates the index page to blog date directory for Sculpin.' + version: null + updated: '2016-09-03' + stars: 1 + archived: false + abandoned: false + - + name: 'Commonmark Bundle' + package: bcremer/sculpin-commonmark-bundle + repository: 'https://github.com/bcremer/sculpin-commonmark-bundle' + description: 'Sculpin league/commonmark bundle' + version: 0.5.0 + updated: '2021-11-08' + stars: 3 + archived: false + abandoned: false + - + name: 'Content Generator Bundle' + package: opdavies/sculpin-content-generator-bundle + repository: 'https://github.com/opdavies/sculpin-content-generator-bundle' + description: null + version: 1.1.1 + updated: '2018-02-18' + stars: 0 + archived: false + abandoned: false + - + name: 'Contentful Bundle' + package: airtonzanon/sculpin-contentful-bundle + repository: 'https://github.com/airtonzanon/sculpin-contentful-bundle' + description: 'Fetch contentful content and dump the markdowns as posts for sculpin' + version: 0.0.10 + updated: '2024-02-27' + stars: 1 + archived: false + abandoned: false + - + name: 'Date Navigation Bundle' + package: jbouzekri/sculpin-date-navigation-bundle + repository: 'https://github.com/jbouzekri/SculpinDateNavigationBundle' + description: 'Generate date navigation block (with pages) in Sculpin' + version: 1.0.0 + updated: '2014-07-21' + stars: 1 + archived: false + abandoned: false + - + name: 'Dflydev Twig GitHub Gist Bundle' + package: dflydev/twig-github-gist-sculpin-bundle + repository: 'https://github.com/dflydev/dflydev-twig-gitHub-gist-sculpin-bundle' + description: 'Twig GitHub Gist Sculpin Bundle' + version: null + updated: '2012-08-15' + stars: 2 + archived: false + abandoned: false + - + name: 'Editor Bundle' + package: mavimo/sculpin-editor-bundle + repository: 'https://github.com/mavimo/sculpin-editor-bundle' + description: 'Sculpin Editor Bundle' + version: null + updated: '2017-05-23' + stars: 5 + archived: false + abandoned: false + - + name: 'Facebook Importer Bundle' + package: technikh/sculpin-fb-bundle + repository: 'https://github.com/TechNikh/Sculpin-Facebook-Importer-Bundle' + description: null + version: null + updated: '2022-05-20' + stars: 1 + archived: false + abandoned: false + - + name: 'Fillet Bundle' + package: dragonmantank/fillet-sculpin-bundle + repository: 'https://github.com/dragonmantank/fillet-sculpin-bundle' + description: 'Bundle wrapper for Fillet' + version: v0.1.0 + updated: '2015-09-01' + stars: 0 + archived: false + abandoned: false + - + name: 'Gist Embed Bundle' + package: opdavies/sculpin-gist-embed-bundle + repository: 'https://github.com/opdavies/sculpin-gist-embed-bundle' + description: 'Allows for embedding GitHub Gists into a Sculpin site.' + version: 0.1.0 + updated: '2018-02-18' + stars: 0 + archived: false + abandoned: false + - + name: 'Gulp Bundle' + package: petemc/sculpin-gulp-bundle + repository: 'https://github.com/petemcfarlane/sculpin-gulp-bundle' + description: 'Runs a gulp task called `sculpin` from your `gulpfile.js` after running the `sculpin generate` command.' + version: v0.0.1 + updated: '2016-04-13' + stars: 0 + archived: false + abandoned: false + - + name: 'Latest Posts Bundle' + package: null + repository: 'https://github.com/opdavies/sculpin-latest-posts-bundle' + description: 'A custom bundle for displaying the latest posts on a Sculpin site.' + version: null + updated: '2015-08-08' + stars: 0 + archived: false + abandoned: false + - + name: 'Less Bundle' + package: bcremer/sculpin-less-bundle + repository: 'https://github.com/bcremer/sculpin-less-bundle' + description: 'Sculpin LESS bundle' + version: 0.3.0 + updated: '2023-01-09' + stars: 6 + archived: false + abandoned: false + - + name: 'Lists Bundle' + package: delssajri/sculpin-lists-bundle + repository: 'https://github.com/delssajri/sculpin-lists-bundle' + description: null + version: null + updated: '2016-03-14' + stars: 0 + archived: false + abandoned: false + - + name: 'Mt Haml Bundle' + package: fervo/sculpin-mthaml-bundle + repository: 'https://github.com/fervo/SculpinMtHamlBundle' + description: 'MtHaml Bundle for Sculpin' + version: null + updated: '2016-01-12' + stars: 0 + archived: false + abandoned: false + - + name: 'OEmbed Bundle' + package: bangpound/sculpin-oembed-bundle + repository: 'https://github.com/bangpound/Sculpin-oEmbed-Bundle' + description: 'oEmbed bundle for Sculpin static site generator' + version: null + updated: '2015-07-11' + stars: 0 + archived: false + abandoned: false + - + name: 'Pages Bundle' + package: fab/sculpin-pages-bundle + repository: 'https://github.com/fabarea/sculpin-pages-bundle' + description: 'Sculpin Pages Bundle' + version: 1.0.0 + updated: '2015-03-03' + stars: 5 + archived: false + abandoned: false + - + name: 'Press Bundle' + package: null + repository: 'https://github.com/simensen/press-sculpin-bundle' + description: '[WIP] Quick example of building a Sculpin bundle to add another type of content' + version: null + updated: '2013-12-26' + stars: 0 + archived: false + abandoned: false + - + name: 'Projects Bundle' + package: mavimo/sculpin-projects-bundle + repository: 'https://github.com/mavimo/sculpin-projects-bundle' + description: 'Sculpin Projects Bundle' + version: null + updated: '2014-01-02' + stars: 0 + archived: false + abandoned: false + - + name: 'Redirect Bundle' + package: mavimo/sculpin-redirect-bundle + repository: 'https://github.com/mavimo/sculpin-redirect-bundle' + description: 'Sculpin Redirect Bundle' + version: null + updated: '2015-07-26' + stars: 6 + archived: false + abandoned: false + - + name: 'Related Content Bundle' + package: wjzijderveld/sculpin-related-content-bundle + repository: 'https://github.com/wjzijderveld/SculpinRelatedContentBundle' + description: 'Sculpin Bundle to generate related content data' + version: 1.0.1 + updated: '2014-07-31' + stars: 4 + archived: true + abandoned: true + - + name: 'Scss Bundle' + package: devworks/sculpin-scss-bundle + repository: 'https://github.com/devworks/sculpin-scss-bundle' + description: 'Sculpin SCSS Bundle' + version: '0.1' + updated: '2019-02-20' + stars: 0 + archived: false + abandoned: false + - + name: 'Search Bundle' + package: jbouzekri/sculpin-search-bundle + repository: 'https://github.com/jbouzekri/SculpinSearchBundle' + description: 'Provides search in static site via indextank search engine' + version: 1.0.1 + updated: '2014-07-21' + stars: 1 + archived: false + abandoned: false + - + name: 'Show Layout Bundle' + package: dupuchba/sculpin-show-layout-bundle + repository: 'https://github.com/dupuchba/sculpin-show-layout-bundle' + description: '[WIP] Sculpin bundle that attempt to display original layout to the user' + version: null + updated: '2015-03-25' + stars: 0 + archived: false + abandoned: false + - + name: 'Sitemap Bundle' + package: nickpeirson/sculpin-sitemap-bundle + repository: 'https://github.com/nickpeirson/sculpin-sitemap-bundle' + description: null + version: 0.1.1 + updated: '2020-08-18' + stars: 3 + archived: false + abandoned: false + - + name: 'Tag Cloud Bundle' + package: jbouzekri/sculpin-tag-cloud-bundle + repository: 'https://github.com/jbouzekri/SculpinTagCloudBundle' + description: 'Generate a tag cloud in sculpin' + version: '1.2' + updated: '2015-07-12' + stars: 2 + archived: false + abandoned: false + - + name: 'Twig Markdown Bundle' + package: opdavies/sculpin-twig-markdown-bundle + repository: 'https://github.com/emrysal/sculpin-twig-markdown-bundle' + description: null + version: 0.2.0 + updated: '2021-05-06' + stars: 0 + archived: false + abandoned: false + - + name: 'Webpack Encore Bundle' + package: niels-nijens/sculpin-webpack-encore-bundle + repository: 'https://github.com/jacktonkin/sculpin-webpack-encore-bundle' + description: 'Sculpin integration with Webpack Encore!' + version: null + updated: '2023-12-05' + stars: 0 + archived: false + abandoned: false +ignored: + - 'https://github.com/sculpin/posts-bundle' diff --git a/app/config/sculpin_site.yml b/app/config/sculpin_site.yml index 1294c11..2bf898a 100644 --- a/app/config/sculpin_site.yml +++ b/app/config/sculpin_site.yml @@ -1,3 +1,6 @@ +imports: + - bundles.yml + title: Sculpin subtitle: PHP Static Site Generator assets_version: 6 diff --git a/composer.json b/composer.json index 4ad5fae..b64715a 100644 --- a/composer.json +++ b/composer.json @@ -8,6 +8,9 @@ } }, "scripts": { + "fetch-bundles": [ + "@php scripts/fetch-bundles.php" + ], "watch": [ "Composer\\Config::disableProcessTimeout", "vendor/bin/sculpin generate --watch --server" diff --git a/docs/community/index.html b/docs/community/index.html index d2040b8..20be65f 100644 --- a/docs/community/index.html +++ b/docs/community/index.html @@ -5,6 +5,7 @@ + Community — Sculpin — PHP Static Site Generator @@ -229,6 +230,91 @@

Blogs/Personal Sites

+

+ +
+ +

Bundles

+ +

Bundles extend Sculpin with extra functionality. Have one you'd like listed +here? Add the sculpin-bundle topic to your repository on GitHub, or +send a Pull Request to the sculpin.io +repository.

+ +

+ +

diff --git a/docs/css/style.css b/docs/css/style.css index a1adb05..f77a131 100644 --- a/docs/css/style.css +++ b/docs/css/style.css @@ -269,6 +269,52 @@ footer.site .copyright { color: #DFE7EA; } +.bundles ul.list-inline { + display: grid; + grid-template-columns: 1fr; + grid-gap: 2vw; +} + +.bundles ul li a { + text-decoration: none; +} + +.bundles li.bundle { + padding: .5em; + text-align: left; +} + +.bundles .bundle .title { + font-size: 1.2em; +} + +.bundles .bundle .source { + color: #C0CFD5; + font-size: .75em; +} +.bundles .bundle .source:hover { + color: #DFE7EA; +} + +.bundles .bundle .description, +.bundles .bundle .meta { + font-size: .8em; + margin: .5em 0 0; +} + +.bundles .bundle .meta { + color: #C0CFD5; +} + +.bundles .bundle .meta span + span:before { + content: "\00b7 "; +} + +.bundles .bundle .meta .flag { + color: #C1543F; + text-transform: uppercase; +} + section.content { color: #547989; @@ -611,6 +657,10 @@ only screen and ( min-resolution: 2dppx) and (min-width: 600px) .powered-by .powered-by-site .source { font-size: .5em; } + + .bundles ul.list-inline { + grid-template-columns: repeat(2, 1fr); + } } @media only screen and (min-width: 992px) { diff --git a/docs/documentation/additional-skeletons/index.html b/docs/documentation/additional-skeletons/index.html index 02c4afa..773ff36 100644 --- a/docs/documentation/additional-skeletons/index.html +++ b/docs/documentation/additional-skeletons/index.html @@ -5,6 +5,7 @@ + Additional Skeletons — Sculpin — PHP Static Site Generator diff --git a/docs/documentation/basic-project/index.html b/docs/documentation/basic-project/index.html index 6d45559..718155b 100644 --- a/docs/documentation/basic-project/index.html +++ b/docs/documentation/basic-project/index.html @@ -5,6 +5,7 @@ + Basic Project — Sculpin — PHP Static Site Generator diff --git a/docs/documentation/bundles/SculpinPostsBundle/index.html b/docs/documentation/bundles/SculpinPostsBundle/index.html index 5c484ec..ae1c81c 100644 --- a/docs/documentation/bundles/SculpinPostsBundle/index.html +++ b/docs/documentation/bundles/SculpinPostsBundle/index.html @@ -5,6 +5,7 @@ + SculpinPostsBundle — Sculpin — PHP Static Site Generator diff --git a/docs/documentation/configuration/index.html b/docs/documentation/configuration/index.html index b233517..6073603 100644 --- a/docs/documentation/configuration/index.html +++ b/docs/documentation/configuration/index.html @@ -5,6 +5,7 @@ + Configuration — Sculpin — PHP Static Site Generator diff --git a/docs/documentation/content-types/custom-types/index.html b/docs/documentation/content-types/custom-types/index.html index 91ba86c..9411f0f 100644 --- a/docs/documentation/content-types/custom-types/index.html +++ b/docs/documentation/content-types/custom-types/index.html @@ -5,6 +5,7 @@ + Custom Types — Sculpin — PHP Static Site Generator diff --git a/docs/documentation/content-types/index.html b/docs/documentation/content-types/index.html index 9522cf7..5b6b34f 100644 --- a/docs/documentation/content-types/index.html +++ b/docs/documentation/content-types/index.html @@ -5,6 +5,7 @@ + Content Types — Sculpin — PHP Static Site Generator diff --git a/docs/documentation/content-types/posts/index.html b/docs/documentation/content-types/posts/index.html index 0a37822..9dc7725 100644 --- a/docs/documentation/content-types/posts/index.html +++ b/docs/documentation/content-types/posts/index.html @@ -5,6 +5,7 @@ + Posts — Sculpin — PHP Static Site Generator diff --git a/docs/documentation/converters/index.html b/docs/documentation/converters/index.html index ccfb3e5..558b6c4 100644 --- a/docs/documentation/converters/index.html +++ b/docs/documentation/converters/index.html @@ -5,6 +5,7 @@ + Converters — Sculpin — PHP Static Site Generator diff --git a/docs/documentation/extending-sculpin/community-extensions/index.html b/docs/documentation/extending-sculpin/community-extensions/index.html index 5184672..81c5aef 100644 --- a/docs/documentation/extending-sculpin/community-extensions/index.html +++ b/docs/documentation/extending-sculpin/community-extensions/index.html @@ -5,6 +5,7 @@ + Community Extensions — Sculpin — PHP Static Site Generator diff --git a/docs/documentation/extending-sculpin/configuration/index.html b/docs/documentation/extending-sculpin/configuration/index.html index 1910250..bdde57d 100644 --- a/docs/documentation/extending-sculpin/configuration/index.html +++ b/docs/documentation/extending-sculpin/configuration/index.html @@ -5,6 +5,7 @@ + Configuration — Sculpin — PHP Static Site Generator diff --git a/docs/documentation/extending-sculpin/creating-bundles/index.html b/docs/documentation/extending-sculpin/creating-bundles/index.html index 7a9010c..033b0e5 100644 --- a/docs/documentation/extending-sculpin/creating-bundles/index.html +++ b/docs/documentation/extending-sculpin/creating-bundles/index.html @@ -5,6 +5,7 @@ + Creating Bundles — Sculpin — PHP Static Site Generator diff --git a/docs/documentation/extending-sculpin/index.html b/docs/documentation/extending-sculpin/index.html index dfc583b..1cc4c49 100644 --- a/docs/documentation/extending-sculpin/index.html +++ b/docs/documentation/extending-sculpin/index.html @@ -5,6 +5,7 @@ + Extending Sculpin — Sculpin — PHP Static Site Generator diff --git a/docs/documentation/extending-sculpin/lifecycle/index.html b/docs/documentation/extending-sculpin/lifecycle/index.html index 63e1444..a330f45 100644 --- a/docs/documentation/extending-sculpin/lifecycle/index.html +++ b/docs/documentation/extending-sculpin/lifecycle/index.html @@ -5,6 +5,7 @@ + Sculpin Lifecycle — Sculpin — PHP Static Site Generator diff --git a/docs/documentation/formatters/index.html b/docs/documentation/formatters/index.html index 5108597..29717dd 100644 --- a/docs/documentation/formatters/index.html +++ b/docs/documentation/formatters/index.html @@ -5,6 +5,7 @@ + Formatters — Sculpin — PHP Static Site Generator diff --git a/docs/documentation/generators/index.html b/docs/documentation/generators/index.html index 0ed9db7..8938615 100644 --- a/docs/documentation/generators/index.html +++ b/docs/documentation/generators/index.html @@ -5,6 +5,7 @@ + Generators — Sculpin — PHP Static Site Generator diff --git a/docs/documentation/index.html b/docs/documentation/index.html index ac8669d..6019670 100644 --- a/docs/documentation/index.html +++ b/docs/documentation/index.html @@ -5,6 +5,7 @@ + Documentation — Sculpin — PHP Static Site Generator diff --git a/docs/documentation/internals/index.html b/docs/documentation/internals/index.html index 7c64d4a..dd0e318 100644 --- a/docs/documentation/internals/index.html +++ b/docs/documentation/internals/index.html @@ -5,6 +5,7 @@ + Internals — Sculpin — PHP Static Site Generator diff --git a/docs/documentation/layouts/index.html b/docs/documentation/layouts/index.html index 2957c68..e54e7ff 100644 --- a/docs/documentation/layouts/index.html +++ b/docs/documentation/layouts/index.html @@ -5,6 +5,7 @@ + Layouts — Sculpin — PHP Static Site Generator diff --git a/docs/documentation/sources/index.html b/docs/documentation/sources/index.html index 5e8cd07..601d11e 100644 --- a/docs/documentation/sources/index.html +++ b/docs/documentation/sources/index.html @@ -5,6 +5,7 @@ + Sources — Sculpin — PHP Static Site Generator diff --git a/docs/documentation/themes/index.html b/docs/documentation/themes/index.html index 0018d9a..97cfec6 100644 --- a/docs/documentation/themes/index.html +++ b/docs/documentation/themes/index.html @@ -5,6 +5,7 @@ + Themes — Sculpin — PHP Static Site Generator diff --git a/docs/download/index.html b/docs/download/index.html index e211e3d..045ff6e 100644 --- a/docs/download/index.html +++ b/docs/download/index.html @@ -5,6 +5,7 @@ + Download — Sculpin — PHP Static Site Generator diff --git a/docs/getstarted/index.html b/docs/getstarted/index.html index 9174bee..36123ea 100644 --- a/docs/getstarted/index.html +++ b/docs/getstarted/index.html @@ -5,6 +5,7 @@ + Get Started — Sculpin — PHP Static Site Generator diff --git a/docs/index.html b/docs/index.html index 79f099a..1ed1306 100644 --- a/docs/index.html +++ b/docs/index.html @@ -5,6 +5,7 @@ + Sculpin — PHP Static Site Generator @@ -271,6 +272,87 @@

Blogs/Personal Sites

+
+ +

Bundles

+

+ Bundles extend Sculpin with extra functionality. Have one you'd like listed here? Add the sculpin-bundle topic to your repository on GitHub, or send a Pull Request to the sculpin.io repository. +

+
+ +
+ diff --git a/docs/mascot/index.html b/docs/mascot/index.html index 8644457..faf5bb4 100644 --- a/docs/mascot/index.html +++ b/docs/mascot/index.html @@ -5,6 +5,7 @@ + Jackson, the Sculpin mascot — Sculpin — PHP Static Site Generator diff --git a/scripts/fetch-bundles.php b/scripts/fetch-bundles.php new file mode 100755 index 0000000..6ddb595 --- /dev/null +++ b/scripts/fetch-bundles.php @@ -0,0 +1,428 @@ +#!/usr/bin/env php + humanise($repository['name']), 'repository' => $repository['html_url']]; + $added[] = $repository['full_name']; + } + } + + // Refresh both lists, including entries that were added by hand and so were + // never returned by a search. + foreach ([&$bundles, &$discovered] as &$list) { + foreach ($list as $url => $entry) { + stderr(sprintf(' %s', $entry['repository'])); + + $list[$url] = refresh($entry, $repositories[strtolower(repositorySlug($entry['repository']))] ?? null); + } + } + unset($list); + + $config['bundles'] = sortByName($bundles); + $config['discovered'] = sortByName($discovered); + $config['ignored'] = array_values($config['ignored'] ?? []); + + $yaml = FILE_HEADER.Yaml::dump($config, 4, 4, Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE); + + if ($dryRun) { + echo $yaml; + } else { + file_put_contents(CONFIG_FILE, $yaml); + } + + stderr(''); + stderr(sprintf('%d listed, %d awaiting review.', count($config['bundles']), count($config['discovered']))); + + if ($added) { + stderr(''); + stderr(sprintf('Newly discovered (review before moving into `bundles`):%s - %s', PHP_EOL, implode(PHP_EOL.' - ', $added))); + } + + return 0; +} + +function loadConfig(): array +{ + if (!file_exists(CONFIG_FILE)) { + return ['bundles' => [], 'discovered' => [], 'ignored' => []]; + } + + return Yaml::parseFile(CONFIG_FILE) ?? []; +} + +/** + * Bring one entry up to date. $repository is the GitHub search result, if the + * repository was returned by one of the searches. + */ +function refresh(array $entry, ?array $repository): array +{ + if (null === $repository) { + $repository = github('repos/'.repositorySlug($entry['repository'])); + } + + if (null === $repository) { + stderr(' ! repository not found on GitHub, leaving as-is'); + + return $entry; + } + + $package = $entry['package'] ?? packageName($repository['full_name']); + $packagist = null !== $package ? packagist($package) : null; + + $fresh = [ + 'description' => trim((string) ($repository['description'] ?? '')) ?: null, + 'version' => null !== $packagist ? latestStableVersion(array_keys($packagist['versions'] ?? [])) : null, + 'updated' => substr((string) $repository['pushed_at'], 0, 10), + 'stars' => (int) $repository['stargazers_count'], + 'archived' => (bool) $repository['archived'], + 'abandoned' => null !== $packagist && ($packagist['abandoned'] ?? null) !== null, + ]; + + foreach (MANAGED_FIELDS as $field) { + // Description is seeded once, then left to the maintainer. + if ('description' === $field && isset($entry['description'])) { + continue; + } + + $entry[$field] = $fresh[$field]; + } + + $entry['package'] = $package; + $entry['repository'] = $repository['html_url']; + + return orderKeys($entry); +} + +function isDiscoverable(array $repository): bool +{ + if (in_array(TOPIC, $repository['topics'] ?? [], true)) { + return true; + } + + foreach (NAME_PATTERNS as $pattern) { + if (!preg_match($pattern, $repository['name'])) { + return false; + } + } + + return true; +} + +/** + * @return array + */ +function searchRepositories(string $query): array +{ + $repositories = []; + + for ($page = 1; $page <= 10; ++$page) { + $response = github('search/repositories?'.http_build_query([ + 'q' => $query, + 'per_page' => 100, + 'page' => $page, + ])); + + if (null === $response || empty($response['items'])) { + break; + } + + $repositories = array_merge($repositories, $response['items']); + + if (count($repositories) >= (int) $response['total_count']) { + break; + } + } + + return $repositories; +} + +/** + * Read the Composer package name out of a repository's composer.json. + */ +function packageName(string $slug): ?string +{ + $response = github('repos/'.$slug.'/contents/composer.json'); + + if (null === $response || !isset($response['content'])) { + return null; + } + + $composer = json_decode((string) base64_decode($response['content'], true), true); + + return $composer['name'] ?? null; +} + +function packagist(string $package): ?array +{ + $response = get('https://packagist.org/packages/'.$package.'.json'); + + return $response['package'] ?? null; +} + +function latestStableVersion(array $versions): ?string +{ + $stable = array_values(array_filter($versions, static function (string $version): bool { + return (bool) preg_match('/^v?\d+(\.\d+)*$/', $version); + })); + + if (!$stable) { + return null; + } + + usort($stable, 'version_compare'); + + return (string) end($stable); +} + +function github(string $path): ?array +{ + $headers = ['Accept: application/vnd.github.mercy-preview+json']; + + if ($token = getenv('GITHUB_TOKEN')) { + $headers[] = 'Authorization: token '.$token; + } + + return get('https://api.github.com/'.$path, $headers); +} + +function get(string $url, array $headers = []): ?array +{ + static $handle; + + $handle = $handle ?: curl_init(); + + curl_setopt_array($handle, [ + CURLOPT_URL => $url, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_TIMEOUT => 30, + CURLOPT_USERAGENT => USER_AGENT, + CURLOPT_HTTPHEADER => $headers, + ]); + + $body = curl_exec($handle); + $status = (int) curl_getinfo($handle, CURLINFO_HTTP_CODE); + + if (false === $body) { + stderr(sprintf(' ! %s: %s', $url, curl_error($handle))); + + return null; + } + + if (403 === $status || 429 === $status) { + stderr(' ! rate limited, sleeping for 60s'); + sleep(60); + + return get($url, $headers); + } + + if (200 !== $status) { + return null; + } + + return json_decode((string) $body, true); +} + +/** + * @param array $entries + * + * @return array + */ +function indexByRepository(array $entries): array +{ + $indexed = []; + + foreach ($entries as $entry) { + if (isset($entry['repository'])) { + $indexed[normaliseRepositoryUrl($entry['repository'])] = $entry; + } + } + + return $indexed; +} + +/** + * The file is kept in alphabetical order so that it stays easy to scan and so + * that a refresh produces a minimal diff. Display order is a separate concern, + * handled in source/_views/includes/bundles.html. + */ +function sortByName(array $entries): array +{ + $entries = array_values($entries); + + usort($entries, static function (array $a, array $b): int { + return strcasecmp((string) ($a['name'] ?? ''), (string) ($b['name'] ?? '')); + }); + + return $entries; +} + +function orderKeys(array $entry): array +{ + $order = ['name', 'package', 'repository', 'description', 'version', 'updated', 'stars', 'archived', 'abandoned']; + + $ordered = []; + foreach ($order as $key) { + if (array_key_exists($key, $entry)) { + $ordered[$key] = $entry[$key]; + } + } + + return $ordered + $entry; +} + +function normaliseRepositoryUrl(string $url): string +{ + return rtrim(strtolower($url), '/'); +} + +function repositorySlug(string $url): string +{ + return trim((string) parse_url($url, PHP_URL_PATH), '/'); +} + +/** + * "sculpin-twig-markdown-bundle" => "Twig Markdown Bundle" + * "SculpinRelatedContentBundle" => "Related Content Bundle" + */ +function humanise(string $name): string +{ + if (!preg_match('/[-_]/', $name)) { + $name = (string) preg_replace('/(? +
    + {% for bundle in site.bundles|filter(bundle => not bundle.abandoned)|sort((a, b) => b.updated <=> a.updated) %} +
  • + {{ bundle.name }} + + GitHub src + + {% if bundle.description %} +

    {{ bundle.description }}

    + {% endif %} + +

    + {% if bundle.version %} + {{ bundle.version }} + {% endif %} + + {% if bundle.updated %} + Updated {{ bundle.updated|date('M Y') }} + {% endif %} + + {% if bundle.archived %} + Archived + {% endif %} +

    +
  • + {% endfor %} +
+ diff --git a/source/community.md b/source/community.md index 58ca827..afa400e 100644 --- a/source/community.md +++ b/source/community.md @@ -42,3 +42,14 @@ powered site you'd like to showcase here? Send a Pull Request to the GitHub, or reach out to @sculpin@phpc.social on Mastodon! {% include 'includes/powered-by.html' %} + +
+ +## Bundles + +Bundles extend Sculpin with extra functionality. Have one you'd like listed +here? Add the sculpin-bundle topic to your repository on GitHub, or +send a Pull Request to the sculpin.io +repository. + +{% include 'includes/bundles.html' %} diff --git a/source/css/style.css b/source/css/style.css index a1adb05..f77a131 100644 --- a/source/css/style.css +++ b/source/css/style.css @@ -269,6 +269,52 @@ footer.site .copyright { color: #DFE7EA; } +.bundles ul.list-inline { + display: grid; + grid-template-columns: 1fr; + grid-gap: 2vw; +} + +.bundles ul li a { + text-decoration: none; +} + +.bundles li.bundle { + padding: .5em; + text-align: left; +} + +.bundles .bundle .title { + font-size: 1.2em; +} + +.bundles .bundle .source { + color: #C0CFD5; + font-size: .75em; +} +.bundles .bundle .source:hover { + color: #DFE7EA; +} + +.bundles .bundle .description, +.bundles .bundle .meta { + font-size: .8em; + margin: .5em 0 0; +} + +.bundles .bundle .meta { + color: #C0CFD5; +} + +.bundles .bundle .meta span + span:before { + content: "\00b7 "; +} + +.bundles .bundle .meta .flag { + color: #C1543F; + text-transform: uppercase; +} + section.content { color: #547989; @@ -611,6 +657,10 @@ only screen and ( min-resolution: 2dppx) and (min-width: 600px) .powered-by .powered-by-site .source { font-size: .5em; } + + .bundles ul.list-inline { + grid-template-columns: repeat(2, 1fr); + } } @media only screen and (min-width: 992px) { diff --git a/source/index.html b/source/index.html index b0c16a2..5b312f1 100644 --- a/source/index.html +++ b/source/index.html @@ -71,5 +71,13 @@

Powered by Sculpin

{% include 'includes/powered-by.html' %} +
+ +

Bundles

+

+ Bundles extend Sculpin with extra functionality. Have one you'd like listed here? Add the sculpin-bundle topic to your repository on GitHub, or send a Pull Request to the sculpin.io repository. +

+ {% include 'includes/bundles.html' %} +