Compare commits
3 Commits
main
...
v1.1.0-nex
Author | SHA1 | Date | |
---|---|---|---|
|
7fb87741a2 | ||
|
602befcb54 | ||
|
791de9ab50 |
21
.eslintrc
Normal file
21
.eslintrc
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 2020,
|
||||||
|
"sourceType": "module"
|
||||||
|
},
|
||||||
|
"env": {
|
||||||
|
"es6": true,
|
||||||
|
"node": true
|
||||||
|
},
|
||||||
|
"extends": [
|
||||||
|
"prettier/@typescript-eslint",
|
||||||
|
"plugin:@typescript-eslint/recommended",
|
||||||
|
"plugin:prettier/recommended"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"@typescript-eslint/no-var-requires": "off",
|
||||||
|
"prefer-rest-params": "off",
|
||||||
|
"@typescript-eslint/no-non-null-assertion": "off"
|
||||||
|
}
|
||||||
|
}
|
17
.eslintrc.js
17
.eslintrc.js
@@ -1,17 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
root: true,
|
|
||||||
parserOptions: {
|
|
||||||
ecmaVersion: 2022,
|
|
||||||
sourceType: 'module',
|
|
||||||
},
|
|
||||||
env: {
|
|
||||||
node: true,
|
|
||||||
},
|
|
||||||
parser: '@typescript-eslint/parser',
|
|
||||||
plugins: ['@typescript-eslint'],
|
|
||||||
extends: [
|
|
||||||
'eslint:recommended',
|
|
||||||
'plugin:prettier/recommended',
|
|
||||||
'plugin:@typescript-eslint/recommended',
|
|
||||||
],
|
|
||||||
};
|
|
39
.github/workflows/dependabot-automerge.yml
vendored
39
.github/workflows/dependabot-automerge.yml
vendored
@@ -1,39 +0,0 @@
|
|||||||
name: Dependabot auto-merge
|
|
||||||
|
|
||||||
on: pull_request_target
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
check:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: ${{ github.actor == 'dependabot[bot]' }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: lts/*
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.GH_TOKEN }}
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: npm i
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: npm run build
|
|
||||||
|
|
||||||
dependabot:
|
|
||||||
needs: [check]
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: ${{ github.actor == 'dependabot[bot]' }}
|
|
||||||
steps:
|
|
||||||
- name: Dependabot metadata
|
|
||||||
id: metadata
|
|
||||||
uses: dependabot/fetch-metadata@v1.6.0
|
|
||||||
with:
|
|
||||||
github-token: '${{ secrets.GH_TOKEN }}'
|
|
||||||
- name: Enable auto-merge for Dependabot PRs
|
|
||||||
if: ${{ steps.metadata.outputs.update-type == 'version-update:semver-patch' || steps.metadata.outputs.update-type == 'version-update:semver-minor' }}
|
|
||||||
run: gh pr merge --auto --merge "$PR_URL"
|
|
||||||
env:
|
|
||||||
PR_URL: ${{github.event.pull_request.html_url}}
|
|
||||||
GITHUB_TOKEN: ${{secrets.GH_TOKEN }}
|
|
21
.github/workflows/merge.yml
vendored
Normal file
21
.github/workflows/merge.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
name: Run tests
|
||||||
|
|
||||||
|
on: [pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest]
|
||||||
|
node-version: [16.x]
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node-version }}
|
||||||
|
- run: npm it
|
30
.github/workflows/release.yml
vendored
30
.github/workflows/release.yml
vendored
@@ -3,16 +3,34 @@ name: Release
|
|||||||
on: [workflow_dispatch]
|
on: [workflow_dispatch]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
test:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest]
|
||||||
|
node-version: [16.x]
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node-version }}
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm it
|
||||||
|
|
||||||
release:
|
release:
|
||||||
|
needs: [test]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
node-version: 16
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GH_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm i
|
run: npm i
|
||||||
@@ -21,8 +39,8 @@ jobs:
|
|||||||
run: npm run build
|
run: npm run build
|
||||||
|
|
||||||
- name: Semantic release
|
- name: Semantic release
|
||||||
uses: codfish/semantic-release-action@v3
|
uses: codfish/semantic-release-action@v1
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
GIT_AUTHOR_NAME: '${{ github.actor }}'
|
GIT_AUTHOR_NAME: '${{ github.actor }}'
|
||||||
GIT_COMMITTER_NAME: '${{ github.actor }}'
|
GIT_COMMITTER_NAME: '${{ github.actor }}'
|
||||||
|
@@ -10,20 +10,15 @@
|
|||||||
[
|
[
|
||||||
"@semantic-release/git",
|
"@semantic-release/git",
|
||||||
{
|
{
|
||||||
"assets": [
|
"assets": ["CHANGELOG.md", "package.json", "package-lock.json"]
|
||||||
"index.js",
|
|
||||||
"CHANGELOG.md",
|
|
||||||
"package.json",
|
|
||||||
"package-lock.json"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"@semantic-release/github",
|
"@semantic-release/github",
|
||||||
[
|
[
|
||||||
"@saithodev/semantic-release-backmerge",
|
"@saithodev/semantic-release-backmerge",
|
||||||
{
|
{
|
||||||
"backmergeBranches": [{ "from": "main", "to": "next" }],
|
"branches": [{ "from": "main", "to": "next" }],
|
||||||
"clearWorkspace": true
|
"backmergeStrategy": "rebase"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
61
CHANGELOG.md
61
CHANGELOG.md
@@ -1,64 +1,3 @@
|
|||||||
# [2.2.0](https://github.com/simonecorsi/mawesome/compare/v2.1.0...v2.2.0) (2022-07-06)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* compact by topic check ([49f6687](https://github.com/simonecorsi/mawesome/commit/49f66875e34ef186f183e370979ef751694d08b2))
|
|
||||||
* compact by topic check ([01cfb39](https://github.com/simonecorsi/mawesome/commit/01cfb39a41a2f60c709fd6dd6a46073149d96c20))
|
|
||||||
* docs ([4d7eef0](https://github.com/simonecorsi/mawesome/commit/4d7eef0991d896027ac464a40edfc564ced6bdd2))
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* adds compacted by topics ([30fa351](https://github.com/simonecorsi/mawesome/commit/30fa351b37a04357303f59c79a90f92219f8072c))
|
|
||||||
* adds compacted by topics ([9dfa1f3](https://github.com/simonecorsi/mawesome/commit/9dfa1f3dae6c69033c682211c0e6bd9dc5a81eaf))
|
|
||||||
|
|
||||||
# [2.1.0](https://github.com/simonecorsi/mawesome/compare/v2.0.0...v2.1.0) (2022-05-11)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* removes console.log ([7841262](https://github.com/simonecorsi/mawesome/commit/7841262e741f05debb7ffe6fed636a508a8f7c12))
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* removes github-token need ([0dc2a51](https://github.com/simonecorsi/mawesome/commit/0dc2a51ddf3cf93414afd674ed3c34ec681f3e4b))
|
|
||||||
|
|
||||||
# [2.0.0](https://github.com/simonecorsi/mawesome/compare/v1.0.45...v2.0.0) (2022-05-11)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* fixes default template path ([6cd9c30](https://github.com/simonecorsi/mawesome/commit/6cd9c30b20acb0789668b9fd4cdbace2cb52d3ce))
|
|
||||||
* fixes tsc build ([ec10b79](https://github.com/simonecorsi/mawesome/commit/ec10b79a91bc5894d35b80026d3e216420e0721a))
|
|
||||||
* normalize template file loadup ([445f562](https://github.com/simonecorsi/mawesome/commit/445f562fb50567d995f0d080d4267fc8d494731b))
|
|
||||||
* pre-tags ([d1d4edd](https://github.com/simonecorsi/mawesome/commit/d1d4edd104affc69984905c8408e859c25c58443))
|
|
||||||
* removes unused tests ([a141d23](https://github.com/simonecorsi/mawesome/commit/a141d23972c31b3dbd7e9841168219ad42fa7a18))
|
|
||||||
* reworking files ([19c4c8f](https://github.com/simonecorsi/mawesome/commit/19c4c8f761b244ddccbc445cc34078bf932559d2))
|
|
||||||
* updates gh-star-fetch ([3ec4b7c](https://github.com/simonecorsi/mawesome/commit/3ec4b7cd53c1fe885a51fb64279047a201d535dc))
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* release major ([51a4359](https://github.com/simonecorsi/mawesome/commit/51a4359d983be4c842410f0c62104fca1b28252f))
|
|
||||||
* update to node16 ([e1f37af](https://github.com/simonecorsi/mawesome/commit/e1f37af978ebcb7f770949476ac7d6bc788a1fc2))
|
|
||||||
* updates deps ([7ade95d](https://github.com/simonecorsi/mawesome/commit/7ade95df8566a59145652165400cddfd1afa4bed))
|
|
||||||
* using gh-star-fetch ([346ba5d](https://github.com/simonecorsi/mawesome/commit/346ba5d4b7ba6a71bab99f2dbe3c2d010beb67d5))
|
|
||||||
* **template:** adds templates ([2c742b8](https://github.com/simonecorsi/mawesome/commit/2c742b820558fd715de987178303c460f5871c29)), closes [#14](https://github.com/simonecorsi/mawesome/issues/14)
|
|
||||||
|
|
||||||
|
|
||||||
### BREAKING CHANGES
|
|
||||||
|
|
||||||
* refactored code
|
|
||||||
|
|
||||||
# [1.1.0-next.2](https://github.com/simonecorsi/mawesome/compare/v1.1.0-next.1...v1.1.0-next.2) (2022-05-09)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* using gh-star-fetch ([a8b6577](https://github.com/simonecorsi/mawesome/commit/a8b657735b9879636cc039d79fddcdca33ccf38e))
|
|
||||||
|
|
||||||
# [1.1.0-next.1](https://github.com/simonecorsi/mawesome/compare/v1.0.45...v1.1.0-next.1) (2022-04-13)
|
# [1.1.0-next.1](https://github.com/simonecorsi/mawesome/compare/v1.0.45...v1.1.0-next.1) (2022-04-13)
|
||||||
|
|
||||||
|
|
||||||
|
36
README.md
36
README.md
@@ -8,12 +8,11 @@ You can see an example of the output at my own [simonecorsi/awesome](https://git
|
|||||||
|
|
||||||
<!-- toc -->
|
<!-- toc -->
|
||||||
|
|
||||||
|
- [Table of Contents](#table-of-contents)
|
||||||
- [Documentation](#documentation)
|
- [Documentation](#documentation)
|
||||||
- [Requirements](#requirements)
|
- [Requirements](#requirements)
|
||||||
- [Configuration](#configuration)
|
- [Configuration](#configuration)
|
||||||
- [`api-token`](#api-token)
|
- [`api-token`](#api-token)
|
||||||
- [`compact-by-topic`](#compact-by-topic)
|
|
||||||
- [`template-path`](#template-path)
|
|
||||||
- [Example workflow](#example-workflow)
|
- [Example workflow](#example-workflow)
|
||||||
|
|
||||||
<!-- tocstop -->
|
<!-- tocstop -->
|
||||||
@@ -22,44 +21,32 @@ You can see an example of the output at my own [simonecorsi/awesome](https://git
|
|||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
|
|
||||||
- An empty repository
|
- An empty repository
|
||||||
- A personal github api key
|
- A personal github api key
|
||||||
|
|
||||||
### Configuration
|
### Configuration
|
||||||
|
|
||||||
The service can be configured setting the appropriate environment variables or writing an `.env` file.
|
The service can be configured setting the appropriate environment variables or writing an `.env` file.
|
||||||
|
|
||||||
| Variable | Description | Default |
|
| Variable | Description | Default |
|
||||||
| ------------------ | ------------------------------------------------------------------- | -------------------------------- |
|
| -------------- | ------------------------------------------- | -------------------------------- |
|
||||||
| `api-token` | Personal Token is used to avoid rate limit, [read more](#api-token) | `${{ secrets.API_TOKEN }}` |
|
| `api-token` | Personal github api token. | `${{ secrets.API_TOKEN }}` |
|
||||||
| `compact-by-topic` | Render another list in `TOPICS.md` compacted by github topics | `'false'` |
|
| `github-token` | Action Token | `${{ secrets.GITHUB_TOKEN }}` |
|
||||||
| `github-name` | Name used for the commit | Github Action |
|
| `github-name` | Name used for the commit, default to action | Github Action |
|
||||||
| `github-email` | email used for commit | actions@users.noreply.github.com |
|
| `github-email` | email used for commit, default to action | actions@users.noreply.github.com |
|
||||||
| `template-path` | Custom `README.md` template, [read more](#template-path) |
|
|
||||||
| `output-filename` | Output filename | `README.md` |
|
|
||||||
|
|
||||||
#### `api-token`
|
#### `api-token`
|
||||||
|
|
||||||
The Personal API Access Token is mandatory to fetch stars from the API without incurring in Rate Limits.
|
The Personal API Access Token is mandatory to fetch stars from the API without incurring in Rate Limits.
|
||||||
|
|
||||||
You'll have to generate a [personal api token](https://github.com/settings/tokens/new) and then add
|
You'll have to generate a [personal api token](https://github.com/settings/tokens/new) and then add
|
||||||
|
|
||||||
#### `compact-by-topic`
|
|
||||||
|
|
||||||
If `compact-by-topic` is `'true'` it will generate another markdown file `TOPICS.md` whith all stars compacted by their github topics, be aware that this list will be bigger since data is duplicated.
|
|
||||||
|
|
||||||
#### `template-path`
|
|
||||||
|
|
||||||
If you don't like the output (default example [here](./TEMPLATE.ejs) ), you can provide your custom template that will be rendered using [EJS](https://ejs.co/) template engine.
|
|
||||||
|
|
||||||
**Path provided is relative to your current repository directory, if file is not found it will default.**
|
|
||||||
|
|
||||||
## Example workflow
|
## Example workflow
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
name: Update awesome list
|
name: Update awesome list
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 0 * * *'
|
- cron: '0 0 * * *'
|
||||||
@@ -76,4 +63,5 @@ jobs:
|
|||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
github-email: ${{ secrets.USER_EMAIL }}
|
github-email: ${{ secrets.USER_EMAIL }}
|
||||||
github-name: ${{ github.repository_owner }}
|
github-name: ${{ github.repository_owner }}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
21
action.yml
21
action.yml
@@ -4,29 +4,16 @@ branding:
|
|||||||
icon: align-justify
|
icon: align-justify
|
||||||
color: yellow
|
color: yellow
|
||||||
inputs:
|
inputs:
|
||||||
compact-by-topic:
|
github-token:
|
||||||
description: 'Generate another page with output compacted by github topics'
|
description: 'Github token'
|
||||||
default: 'false'
|
required: true
|
||||||
required: false
|
|
||||||
api-token:
|
api-token:
|
||||||
description: 'Personal API Token'
|
description: 'Personal API Token'
|
||||||
required: true
|
required: true
|
||||||
github-name:
|
github-name:
|
||||||
description: 'Name shown in the commit'
|
description: 'Name shown in the commit'
|
||||||
default: 'GitHub Actions'
|
|
||||||
required: false
|
|
||||||
github-email:
|
github-email:
|
||||||
description: 'Email shown in the commit'
|
description: 'Email shown in the commit'
|
||||||
default: 'actions@users.noreply.github.com'
|
|
||||||
required: false
|
|
||||||
template-path:
|
|
||||||
required: false
|
|
||||||
description: 'EJS template path relative to project root directory'
|
|
||||||
default: 'TEMPLATE.ejs'
|
|
||||||
output-filename:
|
|
||||||
description: 'The output file name, default to README.md'
|
|
||||||
required: false
|
|
||||||
default: 'README.md'
|
|
||||||
runs:
|
runs:
|
||||||
using: 'node16'
|
using: 'node12'
|
||||||
main: 'index.js'
|
main: 'index.js'
|
||||||
|
7
ava.config.js
Normal file
7
ava.config.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
/* eslint-disable node/no-unsupported-features/es-syntax */
|
||||||
|
|
||||||
|
export default {
|
||||||
|
files: ['!templates/**/*'],
|
||||||
|
extensions: ['ts'],
|
||||||
|
require: ['ts-node/register/transpile-only'],
|
||||||
|
};
|
17968
package-lock.json
generated
17968
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
65
package.json
65
package.json
@@ -1,15 +1,18 @@
|
|||||||
{
|
{
|
||||||
"name": "mawesome",
|
"name": "mawesome",
|
||||||
"version": "2.2.0",
|
"version": "1.1.0-next.1",
|
||||||
"description": "Generate awesome list from user starred repositories",
|
"description": "Generate awesome list from user starred repositories",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"author": "Simone Corsi<simonecorsi.dev@gmail.com>",
|
"author": "Simone Corsi<simonecorsi.dev@gmail.com>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"test": "nyc --reporter=lcov --reporter=text-summary ava -s -v",
|
||||||
|
"test:watch": "ava -w",
|
||||||
"style:lint": "eslint src --ext .ts",
|
"style:lint": "eslint src --ext .ts",
|
||||||
"style:prettier": "prettier \"src/**/*.ts\" --list-different --write",
|
"style:prettier": "prettier \"src/**/*.ts\" --list-different --write",
|
||||||
"build": "./node_modules/.bin/ncc build src/index.ts -o ./",
|
"build": "./node_modules/.bin/ncc build src/index.ts -o ./",
|
||||||
"dev": "ts-node-dev src/index.ts",
|
"dev": "ts-node-dev src/index.ts",
|
||||||
|
"prerelease": "npm run build",
|
||||||
"prepare": "node prepare.js || echo 'Skipping prepare'"
|
"prepare": "node prepare.js || echo 'Skipping prepare'"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
@@ -22,44 +25,44 @@
|
|||||||
"javascript"
|
"javascript"
|
||||||
],
|
],
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@commitlint/cli": "^18.6.1",
|
"@commitlint/cli": "^16.2.3",
|
||||||
"@commitlint/config-conventional": "^18.6.2",
|
"@commitlint/config-conventional": "^16.2.1",
|
||||||
"@octokit/rest": "^20.0.2",
|
"@octokit/rest": "^18.12.0",
|
||||||
"@octokit/types": "^12.6.0",
|
"@octokit/types": "^6.2.1",
|
||||||
"@saithodev/semantic-release-backmerge": "^4.0.1",
|
"@saithodev/semantic-release-backmerge": "^2.1.2",
|
||||||
"@semantic-release/changelog": "^6.0.3",
|
"@semantic-release/changelog": "^6.0.1",
|
||||||
"@semantic-release/commit-analyzer": "^11.1.0",
|
"@semantic-release/commit-analyzer": "^9.0.2",
|
||||||
"@semantic-release/git": "^10.0.1",
|
"@semantic-release/git": "^10.0.1",
|
||||||
"@semantic-release/github": "^9.2.6",
|
"@semantic-release/github": "^8.0.4",
|
||||||
"@semantic-release/release-notes-generator": "^12.1.0",
|
"@semantic-release/release-notes-generator": "^10.0.3",
|
||||||
"@types/ejs": "^3.1.5",
|
"@types/ejs": "^3.0.5",
|
||||||
"@types/got": "^9.6.12",
|
"@types/got": "^9.6.12",
|
||||||
"@types/node": "^20.11.28",
|
"@types/node": "^14.14.5",
|
||||||
"@types/sinon": "^17.0.3",
|
"@types/sinon": "^9.0.10",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
"@typescript-eslint/eslint-plugin": "^4.33.0",
|
||||||
"@typescript-eslint/parser": "^6.21.0",
|
"@typescript-eslint/parser": "^4.6.0",
|
||||||
"@vercel/ncc": "^0.38.1",
|
"@vercel/ncc": "^0.33.3",
|
||||||
"eslint": "^8.57.0",
|
"ava": "^3.8.2",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint": "^7.17.0",
|
||||||
|
"eslint-config-prettier": "^7.1.0",
|
||||||
"eslint-plugin-node": "^11.1.0",
|
"eslint-plugin-node": "^11.1.0",
|
||||||
"eslint-plugin-prettier": "^5.1.3",
|
"eslint-plugin-prettier": "^3.3.1",
|
||||||
"husky": "^8.0.3",
|
"husky": "^7.0.4",
|
||||||
"lint-staged": "^15.2.2",
|
"lint-staged": "^12.3.7",
|
||||||
"markdown-toc": "^1.2.0",
|
"markdown-toc": "^1.2.0",
|
||||||
"nyc": "^15.0.1",
|
"nyc": "^15.0.1",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^2.0.5",
|
||||||
"sinon": "^17.0.1",
|
"sinon": "^9.2.3",
|
||||||
"ts-node-dev": "^2.0.0",
|
"ts-node-dev": "^1.1.1",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^4.1.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "^1.10.1",
|
"@actions/core": "^1.2.6",
|
||||||
"@actions/exec": "^1.1.1",
|
"@actions/exec": "^1.1.1",
|
||||||
"ejs": "^3.1.9",
|
"ejs": "^3.1.6",
|
||||||
"gh-star-fetch": "^1.5.0",
|
"got": "^11.8.1",
|
||||||
"got": "^11.8.5",
|
"remark": "^13.0.0",
|
||||||
"remark": "^15.0.1",
|
"remark-toc": "^7.0.0"
|
||||||
"remark-toc": "^9.0.0"
|
|
||||||
},
|
},
|
||||||
"volta": {
|
"volta": {
|
||||||
"node": "16.14.2",
|
"node": "16.14.2",
|
||||||
|
11
src/api.ts
Normal file
11
src/api.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import got from 'got';
|
||||||
|
import * as core from '@actions/core';
|
||||||
|
|
||||||
|
const GITHUB_TOKEN = core.getInput('api-token', { required: true });
|
||||||
|
|
||||||
|
export default got.extend({
|
||||||
|
headers: {
|
||||||
|
Authorization: `token ${GITHUB_TOKEN}`,
|
||||||
|
},
|
||||||
|
responseType: 'json',
|
||||||
|
});
|
57
src/git.ts
57
src/git.ts
@@ -2,20 +2,13 @@
|
|||||||
|
|
||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
import * as exec from '@actions/exec';
|
import * as exec from '@actions/exec';
|
||||||
import fs from 'fs/promises';
|
|
||||||
|
|
||||||
const { GITHUB_REPOSITORY, GITHUB_REF } = process.env;
|
const { GITHUB_REPOSITORY, GITHUB_REF } = process.env;
|
||||||
|
|
||||||
const branch = GITHUB_REF?.replace('refs/heads/', '');
|
const branch = GITHUB_REF?.replace('refs/heads/', '');
|
||||||
|
|
||||||
type File = {
|
|
||||||
filename: string;
|
|
||||||
data: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Git {
|
class Git {
|
||||||
constructor() {
|
constructor() {
|
||||||
const githubToken = core.getInput('api-token', { required: true });
|
const githubToken = core.getInput('github-token', { required: true });
|
||||||
core.setSecret(githubToken);
|
core.setSecret(githubToken);
|
||||||
|
|
||||||
const githubName = core.getInput('github-name') || 'GitHub Actions';
|
const githubName = core.getInput('github-name') || 'GitHub Actions';
|
||||||
@@ -41,26 +34,28 @@ class Git {
|
|||||||
return isShallow.trim().replace('\n', '') === 'true';
|
return isShallow.trim().replace('\n', '') === 'true';
|
||||||
};
|
};
|
||||||
|
|
||||||
async exec(command: string): Promise<string> {
|
exec = (command: string): Promise<string> => {
|
||||||
let execOutput = '';
|
return new Promise(async (resolve, reject) => {
|
||||||
|
let execOutput = '';
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
listeners: {
|
listeners: {
|
||||||
stdout: (data: Buffer) => {
|
stdout: (data: Buffer) => {
|
||||||
execOutput += data.toString();
|
execOutput += data.toString();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
};
|
||||||
};
|
|
||||||
|
|
||||||
const exitCode = await exec.exec(`git ${command}`, undefined, options);
|
const exitCode = await exec.exec(`git ${command}`, undefined, options);
|
||||||
|
|
||||||
if (exitCode === 0) {
|
if (exitCode === 0) {
|
||||||
return execOutput;
|
return resolve(execOutput);
|
||||||
} else {
|
} else {
|
||||||
core.error(`Command "git ${command}" exited with code ${exitCode}.`);
|
core.error(`Command "git ${command}" exited with code ${exitCode}.`);
|
||||||
throw new Error(`Command "git ${command}" exited with code ${exitCode}.`);
|
return reject(`Command "git ${command}" exited with code ${exitCode}.`);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
};
|
||||||
|
|
||||||
config = (prop: string, value: string) =>
|
config = (prop: string, value: string) =>
|
||||||
this.exec(`config ${prop} "${value}"`);
|
this.exec(`config ${prop} "${value}"`);
|
||||||
@@ -96,20 +91,6 @@ class Git {
|
|||||||
updateOrigin = (repo: string) => this.exec(`remote set-url origin ${repo}`);
|
updateOrigin = (repo: string) => this.exec(`remote set-url origin ${repo}`);
|
||||||
|
|
||||||
createTag = (tag: string) => this.exec(`tag -a ${tag} -m "${tag}"`);
|
createTag = (tag: string) => this.exec(`tag -a ${tag} -m "${tag}"`);
|
||||||
|
|
||||||
async pushNewFiles(files: File[] = []): Promise<unknown> {
|
|
||||||
if (!files.length) return;
|
|
||||||
|
|
||||||
await this.pull();
|
|
||||||
|
|
||||||
await Promise.all(
|
|
||||||
files.map(({ filename, data }) => fs.writeFile(filename, data))
|
|
||||||
);
|
|
||||||
|
|
||||||
await this.add(files.map(({ filename }) => filename));
|
|
||||||
await this.commit(`chore(updates): updated entries in files`);
|
|
||||||
await this.push();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new Git();
|
export default new Git();
|
||||||
|
114
src/helpers.ts
114
src/helpers.ts
@@ -1,14 +1,28 @@
|
|||||||
|
import fs from 'fs';
|
||||||
import ejs from 'ejs';
|
import ejs from 'ejs';
|
||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
import { remark } from 'remark';
|
import remark from 'remark';
|
||||||
import toc from 'remark-toc';
|
import toc from 'remark-toc';
|
||||||
|
|
||||||
|
import MD_TEMPLATE from './template';
|
||||||
|
import GithubApi from './api';
|
||||||
|
import link from './link';
|
||||||
|
import git from './git';
|
||||||
|
|
||||||
|
import type { PaginationLink, ApiGetStarResponse, Stars, Star } from './types';
|
||||||
|
|
||||||
export const REPO_USERNAME = process.env.GITHUB_REPOSITORY?.split('/')[0];
|
export const REPO_USERNAME = process.env.GITHUB_REPOSITORY?.split('/')[0];
|
||||||
export const API_STARRED_URL = `${process.env.GITHUB_API_URL}/users/${REPO_USERNAME}/starred`;
|
export const API_STARRED_URL = `${process.env.GITHUB_API_URL}/users/${REPO_USERNAME}/starred`;
|
||||||
|
|
||||||
|
const fsp = fs.promises;
|
||||||
|
|
||||||
|
export function wait(time = 200): Promise<void> {
|
||||||
|
return new Promise((resolve) => setTimeout(resolve, time));
|
||||||
|
}
|
||||||
|
|
||||||
export async function renderer(
|
export async function renderer(
|
||||||
data: { [key: string]: unknown },
|
data: { [key: string]: any },
|
||||||
templateString: string
|
templateString = MD_TEMPLATE
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
try {
|
try {
|
||||||
return ejs.render(templateString, data);
|
return ejs.render(templateString, data);
|
||||||
@@ -18,6 +32,74 @@ export async function renderer(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getNextPage(links: PaginationLink[]): string | null {
|
||||||
|
const next = links.find((l) => l.rel === 'next');
|
||||||
|
const last = links.find((l) => l.rel === 'last');
|
||||||
|
if (!next || !last) return null;
|
||||||
|
const matchNext = next.uri.match(/page=([0-9]*)/);
|
||||||
|
const matchLast = last.uri.match(/page=([0-9]*)/);
|
||||||
|
if (!matchNext || !matchLast) return null;
|
||||||
|
if (matchNext[1] === matchLast[1]) return null;
|
||||||
|
return matchNext[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
async function* paginateStars(url: string): AsyncGenerator<Stars> {
|
||||||
|
let nextPage: string | null = '1';
|
||||||
|
while (nextPage) {
|
||||||
|
try {
|
||||||
|
const { headers, body } = await GithubApi.get(url, {
|
||||||
|
searchParams: {
|
||||||
|
page: nextPage,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
yield (body as unknown) as Stars;
|
||||||
|
nextPage = getNextPage(link.parse(headers.link).refs);
|
||||||
|
await wait(1000); // avoid limits
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function apiGetStar(
|
||||||
|
url: string = API_STARRED_URL
|
||||||
|
): Promise<ApiGetStarResponse> {
|
||||||
|
const data: Partial<Star>[] = [];
|
||||||
|
for await (const stars of paginateStars(url)) {
|
||||||
|
for (const star of stars) {
|
||||||
|
data.push({
|
||||||
|
id: star.id,
|
||||||
|
node_id: star.node_id,
|
||||||
|
name: star.name,
|
||||||
|
full_name: star.full_name,
|
||||||
|
owner: {
|
||||||
|
login: star?.owner?.login,
|
||||||
|
id: star?.owner?.id,
|
||||||
|
avatar_url: star?.owner?.avatar_url,
|
||||||
|
url: star?.owner?.url,
|
||||||
|
html_url: star?.owner?.html_url,
|
||||||
|
},
|
||||||
|
html_url: star.html_url,
|
||||||
|
description: star.description,
|
||||||
|
url: star.url,
|
||||||
|
languages_url: star.languages_url,
|
||||||
|
created_at: star.created_at,
|
||||||
|
updated_at: star.updated_at,
|
||||||
|
git_url: star.git_url,
|
||||||
|
ssh_url: star.ssh_url,
|
||||||
|
clone_url: star.clone_url,
|
||||||
|
homepage: star.homepage,
|
||||||
|
stargazers_count: star.stargazers_count,
|
||||||
|
watchers_count: star.watchers_count,
|
||||||
|
language: star.language,
|
||||||
|
topics: star.topics,
|
||||||
|
} as Partial<Star>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (data as unknown) as Stars;
|
||||||
|
}
|
||||||
|
|
||||||
export function generateMd(data: string): Promise<string> {
|
export function generateMd(data: string): Promise<string> {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
remark()
|
remark()
|
||||||
@@ -26,11 +108,31 @@ export function generateMd(data: string): Promise<string> {
|
|||||||
if (error) {
|
if (error) {
|
||||||
core.error('#generateMd');
|
core.error('#generateMd');
|
||||||
core.error(error);
|
core.error(error);
|
||||||
resolve('');
|
return resolve('');
|
||||||
}
|
}
|
||||||
resolve(String(file));
|
return resolve(String(file));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MARKDOWN_FILENAME: string = core.getInput('output-filename');
|
export const MARKDOWN_FILENAME: string =
|
||||||
|
core.getInput('output-filename') || 'README.md';
|
||||||
|
|
||||||
|
type File = {
|
||||||
|
filename: string;
|
||||||
|
data: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function pushNewFiles(files: File[] = []): Promise<any> {
|
||||||
|
if (!files.length) return;
|
||||||
|
|
||||||
|
await git.pull();
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
files.map(({ filename, data }) => fsp.writeFile(filename, data))
|
||||||
|
);
|
||||||
|
|
||||||
|
await git.add(files.map(({ filename }) => filename));
|
||||||
|
await git.commit(`chore(updates): updated entries in files`);
|
||||||
|
await git.push();
|
||||||
|
}
|
||||||
|
92
src/index.ts
92
src/index.ts
@@ -1,85 +1,68 @@
|
|||||||
import path from 'path';
|
|
||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
import { readFile } from 'fs/promises';
|
import { readdir, readFile } from 'fs/promises';
|
||||||
import ghStarFetch, {
|
|
||||||
Options,
|
|
||||||
compactByLanguage,
|
|
||||||
compactByTopic,
|
|
||||||
} from 'gh-star-fetch';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
renderer,
|
renderer,
|
||||||
REPO_USERNAME,
|
REPO_USERNAME,
|
||||||
generateMd,
|
generateMd,
|
||||||
|
pushNewFiles,
|
||||||
MARKDOWN_FILENAME,
|
MARKDOWN_FILENAME,
|
||||||
|
apiGetStar,
|
||||||
} from './helpers';
|
} from './helpers';
|
||||||
import git from './git';
|
|
||||||
|
|
||||||
export async function main() {
|
import type { SortedLanguageList, Stars, Star } from './types';
|
||||||
// set default template
|
|
||||||
let template = await readFile(
|
export async function main(): Promise<any> {
|
||||||
path.resolve(__dirname, './TEMPLATE.ejs'),
|
const results: Stars = await apiGetStar();
|
||||||
'utf8'
|
|
||||||
|
const sortedByLanguages = results.reduce(
|
||||||
|
(acc: SortedLanguageList, val: Star) => {
|
||||||
|
const language = val.language || 'generic';
|
||||||
|
if (!acc[language]) {
|
||||||
|
acc[language] = [val];
|
||||||
|
} else {
|
||||||
|
acc[language].push(val);
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{}
|
||||||
);
|
);
|
||||||
|
|
||||||
// get template if found in the repo
|
// get template if found in the repo
|
||||||
const customTemplatePath = core.getInput('template-path');
|
let template;
|
||||||
core.info(`check if customTemplatePath: ${customTemplatePath} exists`);
|
|
||||||
try {
|
try {
|
||||||
template = await readFile(customTemplatePath, 'utf8');
|
const dir = await readdir('./');
|
||||||
|
core.info(dir.join('\n'));
|
||||||
|
template = await readFile('TEMPLATE.ejs', 'utf8');
|
||||||
|
core.info(template);
|
||||||
} catch {
|
} catch {
|
||||||
core.info("Couldn't find template file, using default");
|
core.warning("Couldn't find template file, using default");
|
||||||
}
|
}
|
||||||
|
|
||||||
const opts: Partial<Options> = {
|
const rendered = await renderer(
|
||||||
accessToken: core.getInput('api-token', { required: true }),
|
|
||||||
};
|
|
||||||
|
|
||||||
const results = await ghStarFetch(opts);
|
|
||||||
|
|
||||||
const files = [];
|
|
||||||
|
|
||||||
const compactedByLanguage = compactByLanguage(results);
|
|
||||||
const byLanguage = await renderer(
|
|
||||||
{
|
{
|
||||||
username: REPO_USERNAME,
|
username: REPO_USERNAME,
|
||||||
stars: Object.entries(compactedByLanguage),
|
stars: Object.entries(sortedByLanguages),
|
||||||
updatedAt: Date.now(),
|
updatedAt: Date.now(),
|
||||||
},
|
},
|
||||||
template
|
template
|
||||||
);
|
);
|
||||||
|
|
||||||
files.push(
|
const markdown: string = await generateMd(rendered);
|
||||||
|
|
||||||
|
await pushNewFiles([
|
||||||
{
|
{
|
||||||
filename: MARKDOWN_FILENAME,
|
filename: MARKDOWN_FILENAME,
|
||||||
data: await generateMd(byLanguage),
|
data: markdown,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
filename: 'data.json',
|
filename: 'data.json',
|
||||||
data: JSON.stringify(compactedByLanguage, null, 2),
|
data: JSON.stringify(sortedByLanguages, null, 2),
|
||||||
}
|
},
|
||||||
);
|
]);
|
||||||
|
|
||||||
if (core.getInput('compact-by-topic') === 'true') {
|
|
||||||
const compactedByTopic = compactByTopic(results);
|
|
||||||
const byTopic = await renderer(
|
|
||||||
{
|
|
||||||
username: REPO_USERNAME,
|
|
||||||
stars: Object.entries(compactedByTopic),
|
|
||||||
updatedAt: Date.now(),
|
|
||||||
},
|
|
||||||
template
|
|
||||||
);
|
|
||||||
files.push({
|
|
||||||
filename: 'TOPICS.md',
|
|
||||||
data: await generateMd(byTopic),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
await git.pushNewFiles(files);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function run(): Promise<void> {
|
export async function run(): Promise<any> {
|
||||||
try {
|
try {
|
||||||
await main();
|
await main();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -87,11 +70,10 @@ export async function run(): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const catchAll = (info: string) => {
|
const catchAll = (info: any) => {
|
||||||
core.setFailed(`#catchAll: ${info}`);
|
core.setFailed(`#catchAll: ${info}`);
|
||||||
core.error(info);
|
|
||||||
};
|
};
|
||||||
process.on('unhandledRejection', catchAll);
|
process.on('unhandledRejection', catchAll);
|
||||||
process.on('uncaughtException', catchAll);
|
process.on('uncaughtException', catchAll);
|
||||||
|
|
||||||
run().catch(core.error);
|
run();
|
||||||
|
384
src/link.js
Normal file
384
src/link.js
Normal file
@@ -0,0 +1,384 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2016 Jonas Hermsmeier
|
||||||
|
* https://github.com/jhermsmeier/node-http-link-header
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* istanbul ignore file */
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var COMPATIBLE_ENCODING_PATTERN = /^utf-?8|ascii|utf-?16-?le|ucs-?2|base-?64|latin-?1$/i;
|
||||||
|
var WS_TRIM_PATTERN = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;
|
||||||
|
var WS_CHAR_PATTERN = /\s|\uFEFF|\xA0/;
|
||||||
|
var WS_FOLD_PATTERN = /\r?\n[\x20\x09]+/g;
|
||||||
|
var DELIMITER_PATTERN = /[;,"]/;
|
||||||
|
var WS_DELIMITER_PATTERN = /[;,"]|\s/;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Token character pattern
|
||||||
|
* @type {RegExp}
|
||||||
|
* @see https://tools.ietf.org/html/rfc7230#section-3.2.6
|
||||||
|
*/
|
||||||
|
var TOKEN_PATTERN = /^[!#$%&'*+\-\.^_`|~\da-zA-Z]+$/;
|
||||||
|
|
||||||
|
var STATE = {
|
||||||
|
IDLE: 1 << 0,
|
||||||
|
URI: 1 << 1,
|
||||||
|
ATTR: 1 << 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
function trim(value) {
|
||||||
|
return value.replace(WS_TRIM_PATTERN, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasWhitespace(value) {
|
||||||
|
return WS_CHAR_PATTERN.test(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function skipWhitespace(value, offset) {
|
||||||
|
while (hasWhitespace(value[offset])) {
|
||||||
|
offset++;
|
||||||
|
}
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
function needsQuotes(value) {
|
||||||
|
return WS_DELIMITER_PATTERN.test(value) || !TOKEN_PATTERN.test(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Link {
|
||||||
|
/**
|
||||||
|
* Link
|
||||||
|
* @constructor
|
||||||
|
* @param {String} [value]
|
||||||
|
* @returns {Link}
|
||||||
|
*/
|
||||||
|
constructor(value) {
|
||||||
|
/** @type {Array} URI references */
|
||||||
|
this.refs = [];
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
this.parse(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get refs with given relation type
|
||||||
|
* @param {String} value
|
||||||
|
* @returns {Array<Object>}
|
||||||
|
*/
|
||||||
|
rel(value) {
|
||||||
|
var links = [];
|
||||||
|
var type = value.toLowerCase();
|
||||||
|
|
||||||
|
for (var i = 0; i < this.refs.length; i++) {
|
||||||
|
if (this.refs[i].rel.toLowerCase() === type) {
|
||||||
|
links.push(this.refs[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return links;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get refs where given attribute has a given value
|
||||||
|
* @param {String} attr
|
||||||
|
* @param {String} value
|
||||||
|
* @returns {Array<Object>}
|
||||||
|
*/
|
||||||
|
get(attr, value) {
|
||||||
|
attr = attr.toLowerCase();
|
||||||
|
|
||||||
|
var links = [];
|
||||||
|
|
||||||
|
for (var i = 0; i < this.refs.length; i++) {
|
||||||
|
if (this.refs[i][attr] === value) {
|
||||||
|
links.push(this.refs[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return links;
|
||||||
|
}
|
||||||
|
|
||||||
|
set(link) {
|
||||||
|
this.refs.push(link);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
has(attr, value) {
|
||||||
|
attr = attr.toLowerCase();
|
||||||
|
|
||||||
|
for (var i = 0; i < this.refs.length; i++) {
|
||||||
|
if (this.refs[i][attr] === value) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
parse(value, offset) {
|
||||||
|
offset = offset || 0;
|
||||||
|
value = offset ? value.slice(offset) : value;
|
||||||
|
|
||||||
|
// Trim & unfold folded lines
|
||||||
|
value = trim(value).replace(WS_FOLD_PATTERN, '');
|
||||||
|
|
||||||
|
var state = STATE.IDLE;
|
||||||
|
var length = value.length;
|
||||||
|
var offset = 0;
|
||||||
|
var ref = null;
|
||||||
|
|
||||||
|
while (offset < length) {
|
||||||
|
if (state === STATE.IDLE) {
|
||||||
|
if (hasWhitespace(value[offset])) {
|
||||||
|
offset++;
|
||||||
|
continue;
|
||||||
|
} else if (value[offset] === '<') {
|
||||||
|
if (ref != null) {
|
||||||
|
ref.rel != null
|
||||||
|
? this.refs.push(...Link.expandRelations(ref))
|
||||||
|
: this.refs.push(ref);
|
||||||
|
}
|
||||||
|
var end = value.indexOf('>', offset);
|
||||||
|
if (end === -1)
|
||||||
|
throw new Error(
|
||||||
|
'Expected end of URI delimiter at offset ' + offset
|
||||||
|
);
|
||||||
|
ref = { uri: value.slice(offset + 1, end) };
|
||||||
|
// this.refs.push( ref )
|
||||||
|
offset = end;
|
||||||
|
state = STATE.URI;
|
||||||
|
} else {
|
||||||
|
throw new Error(
|
||||||
|
'Unexpected character "' + value[offset] + '" at offset ' + offset
|
||||||
|
);
|
||||||
|
}
|
||||||
|
offset++;
|
||||||
|
} else if (state === STATE.URI) {
|
||||||
|
if (hasWhitespace(value[offset])) {
|
||||||
|
offset++;
|
||||||
|
continue;
|
||||||
|
} else if (value[offset] === ';') {
|
||||||
|
state = STATE.ATTR;
|
||||||
|
offset++;
|
||||||
|
} else if (value[offset] === ',') {
|
||||||
|
state = STATE.IDLE;
|
||||||
|
offset++;
|
||||||
|
} else {
|
||||||
|
throw new Error(
|
||||||
|
'Unexpected character "' + value[offset] + '" at offset ' + offset
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if (state === STATE.ATTR) {
|
||||||
|
if (value[offset] === ';' || hasWhitespace(value[offset])) {
|
||||||
|
offset++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var end = value.indexOf('=', offset);
|
||||||
|
if (end === -1)
|
||||||
|
throw new Error('Expected attribute delimiter at offset ' + offset);
|
||||||
|
var attr = trim(value.slice(offset, end)).toLowerCase();
|
||||||
|
var attrValue = '';
|
||||||
|
offset = end + 1;
|
||||||
|
offset = skipWhitespace(value, offset);
|
||||||
|
if (value[offset] === '"') {
|
||||||
|
offset++;
|
||||||
|
while (offset < length) {
|
||||||
|
if (value[offset] === '"') {
|
||||||
|
offset++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (value[offset] === '\\') {
|
||||||
|
offset++;
|
||||||
|
}
|
||||||
|
attrValue += value[offset];
|
||||||
|
offset++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var end = offset + 1;
|
||||||
|
while (!DELIMITER_PATTERN.test(value[end]) && end < length) {
|
||||||
|
end++;
|
||||||
|
}
|
||||||
|
attrValue = value.slice(offset, end);
|
||||||
|
offset = end;
|
||||||
|
}
|
||||||
|
if (ref[attr] && Link.isSingleOccurenceAttr(attr)) {
|
||||||
|
// Ignore multiples of attributes which may only appear once
|
||||||
|
} else if (attr[attr.length - 1] === '*') {
|
||||||
|
ref[attr] = Link.parseExtendedValue(attrValue);
|
||||||
|
} else {
|
||||||
|
attrValue = attr === 'type' ? attrValue.toLowerCase() : attrValue;
|
||||||
|
if (ref[attr] != null) {
|
||||||
|
if (Array.isArray(ref[attr])) {
|
||||||
|
ref[attr].push(attrValue);
|
||||||
|
} else {
|
||||||
|
ref[attr] = [ref[attr], attrValue];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ref[attr] = attrValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch (value[offset]) {
|
||||||
|
case ',':
|
||||||
|
state = STATE.IDLE;
|
||||||
|
break;
|
||||||
|
case ';':
|
||||||
|
state = STATE.ATTR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
offset++;
|
||||||
|
} else {
|
||||||
|
throw new Error('Unknown parser state "' + state + '"');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ref != null) {
|
||||||
|
ref.rel != null
|
||||||
|
? this.refs.push(...Link.expandRelations(ref))
|
||||||
|
: this.refs.push(ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
ref = null;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
toString() {
|
||||||
|
var refs = [];
|
||||||
|
var link = '';
|
||||||
|
var ref = null;
|
||||||
|
|
||||||
|
for (var i = 0; i < this.refs.length; i++) {
|
||||||
|
ref = this.refs[i];
|
||||||
|
link = Object.keys(this.refs[i]).reduce(function (link, attr) {
|
||||||
|
if (attr === 'uri') return link;
|
||||||
|
return link + '; ' + Link.formatAttribute(attr, ref[attr]);
|
||||||
|
}, '<' + ref.uri + '>');
|
||||||
|
refs.push(link);
|
||||||
|
}
|
||||||
|
|
||||||
|
return refs.join(', ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether an encoding can be
|
||||||
|
* natively handled with a `Buffer`
|
||||||
|
* @param {String} value
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
Link.isCompatibleEncoding = function (value) {
|
||||||
|
return COMPATIBLE_ENCODING_PATTERN.test(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
Link.parse = function (value, offset) {
|
||||||
|
return new Link().parse(value, offset);
|
||||||
|
};
|
||||||
|
|
||||||
|
Link.isSingleOccurenceAttr = function (attr) {
|
||||||
|
return (
|
||||||
|
attr === 'rel' ||
|
||||||
|
attr === 'type' ||
|
||||||
|
attr === 'media' ||
|
||||||
|
attr === 'title' ||
|
||||||
|
attr === 'title*'
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Link.isTokenAttr = function (attr) {
|
||||||
|
return attr === 'rel' || attr === 'type' || attr === 'anchor';
|
||||||
|
};
|
||||||
|
|
||||||
|
Link.escapeQuotes = function (value) {
|
||||||
|
return value.replace(/"/g, '\\"');
|
||||||
|
};
|
||||||
|
|
||||||
|
Link.expandRelations = function (ref) {
|
||||||
|
var rels = ref.rel.split(' ');
|
||||||
|
return rels.map(function (rel) {
|
||||||
|
var value = Object.assign({}, ref);
|
||||||
|
value.rel = rel;
|
||||||
|
return value;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses an extended value and attempts to decode it
|
||||||
|
* @internal
|
||||||
|
* @param {String} value
|
||||||
|
* @return {Object}
|
||||||
|
*/
|
||||||
|
Link.parseExtendedValue = function (value) {
|
||||||
|
var parts = /([^']+)?(?:'([^']+)')?(.+)/.exec(value);
|
||||||
|
return {
|
||||||
|
language: parts[2].toLowerCase(),
|
||||||
|
encoding: Link.isCompatibleEncoding(parts[1])
|
||||||
|
? null
|
||||||
|
: parts[1].toLowerCase(),
|
||||||
|
value: Link.isCompatibleEncoding(parts[1])
|
||||||
|
? decodeURIComponent(parts[3])
|
||||||
|
: parts[3],
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format a given extended attribute and it's value
|
||||||
|
* @param {String} attr
|
||||||
|
* @param {Object} data
|
||||||
|
* @return {String}
|
||||||
|
*/
|
||||||
|
Link.formatExtendedAttribute = function (attr, data) {
|
||||||
|
var encoding = (data.encoding || 'utf-8').toUpperCase();
|
||||||
|
var language = data.language || 'en';
|
||||||
|
|
||||||
|
var encodedValue = '';
|
||||||
|
|
||||||
|
if (Buffer.isBuffer(data.value) && Link.isCompatibleEncoding(encoding)) {
|
||||||
|
encodedValue = data.value.toString(encoding);
|
||||||
|
} else if (Buffer.isBuffer(data.value)) {
|
||||||
|
encodedValue = data.value.toString('hex').replace(/[0-9a-f]{2}/gi, '%$1');
|
||||||
|
} else {
|
||||||
|
encodedValue = encodeURIComponent(data.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return attr + '=' + encoding + "'" + language + "'" + encodedValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format a given attribute and it's value
|
||||||
|
* @param {String} attr
|
||||||
|
* @param {String|Object} value
|
||||||
|
* @return {String}
|
||||||
|
*/
|
||||||
|
Link.formatAttribute = function (attr, value) {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
return value
|
||||||
|
.map((item) => {
|
||||||
|
return Link.formatAttribute(attr, item);
|
||||||
|
})
|
||||||
|
.join('; ');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attr[attr.length - 1] === '*' || typeof value !== 'string') {
|
||||||
|
return Link.formatExtendedAttribute(attr, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Link.isTokenAttr(attr)) {
|
||||||
|
value = needsQuotes(value)
|
||||||
|
? '"' + Link.escapeQuotes(value) + '"'
|
||||||
|
: Link.escapeQuotes(value);
|
||||||
|
} else if (needsQuotes(value)) {
|
||||||
|
value = encodeURIComponent(value);
|
||||||
|
// We don't need to escape <SP> <,> <;> within quotes
|
||||||
|
value = value
|
||||||
|
.replace(/%20/g, ' ')
|
||||||
|
.replace(/%2C/g, ',')
|
||||||
|
.replace(/%3B/g, ';');
|
||||||
|
|
||||||
|
value = '"' + value + '"';
|
||||||
|
}
|
||||||
|
|
||||||
|
return attr + '=' + value;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = Link;
|
@@ -1,4 +1,4 @@
|
|||||||
# <%= username %> Awesome List [](https://github.com/sindresorhus/awesome)
|
export default `# <%= username %> Awesome List [](https://github.com/sindresorhus/awesome)
|
||||||
|
|
||||||
> :star: generated with [simonecorsi/mawesome](https://github.com/simonecorsi/mawesome)
|
> :star: generated with [simonecorsi/mawesome](https://github.com/simonecorsi/mawesome)
|
||||||
|
|
||||||
@@ -11,3 +11,4 @@
|
|||||||
<% } %>
|
<% } %>
|
||||||
|
|
||||||
<% } %>
|
<% } %>
|
||||||
|
`;
|
79
test/helpers.spec.ts
Normal file
79
test/helpers.spec.ts
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import test from 'ava';
|
||||||
|
import * as sinon from 'sinon';
|
||||||
|
import fs from 'fs';
|
||||||
|
|
||||||
|
import * as core from '@actions/core';
|
||||||
|
sinon.replace(core, 'getInput', sinon.fake());
|
||||||
|
|
||||||
|
import GithubApi from '../src/api';
|
||||||
|
const GithubApiFake = sinon.fake((rul) => ({
|
||||||
|
body: [],
|
||||||
|
headers: {
|
||||||
|
link:
|
||||||
|
'<https://api.github.com/user/5617452/starred?page=2>; rel="next", <https://api.github.com/user/5617452/starred?page=2>; rel="last"',
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
sinon.replace(GithubApi, 'get', GithubApiFake);
|
||||||
|
|
||||||
|
import Git from '../src/git';
|
||||||
|
const pull = sinon.fake();
|
||||||
|
sinon.replace(Git, 'pull', pull);
|
||||||
|
const add = sinon.fake();
|
||||||
|
sinon.replace(Git, 'add', add);
|
||||||
|
const commit = sinon.fake();
|
||||||
|
sinon.replace(Git, 'commit', commit);
|
||||||
|
const push = sinon.fake();
|
||||||
|
sinon.replace(Git, 'push', push);
|
||||||
|
sinon.replace(Git, 'config', sinon.fake());
|
||||||
|
sinon.replace(Git, 'updateOrigin', sinon.fake());
|
||||||
|
|
||||||
|
const fsp = fs.promises;
|
||||||
|
const writeFile = sinon.fake();
|
||||||
|
sinon.replace(fsp, 'writeFile', writeFile);
|
||||||
|
|
||||||
|
import {
|
||||||
|
wait,
|
||||||
|
renderer,
|
||||||
|
apiGetStar,
|
||||||
|
generateMd,
|
||||||
|
pushNewFiles,
|
||||||
|
} from '../src/helpers';
|
||||||
|
|
||||||
|
test('wait should wait', async (t) => {
|
||||||
|
await wait(200);
|
||||||
|
t.pass();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('renderer should render', async (t) => {
|
||||||
|
const output = await renderer({ variable: 123 }, 'Test: <%= variable %>');
|
||||||
|
t.is(output, 'Test: 123');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('apiGetStar', async (t) => {
|
||||||
|
let stars = await apiGetStar('url');
|
||||||
|
t.true(GithubApiFake.called);
|
||||||
|
t.true(Array.isArray(stars));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('generateMd should create TOC', async (t) => {
|
||||||
|
const tpl = `# title
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
## Javascript
|
||||||
|
`;
|
||||||
|
const result = await generateMd(tpl);
|
||||||
|
t.is(
|
||||||
|
result,
|
||||||
|
`# title\n\n## Table of Contents\n\n* [Javascript](#javascript)\n\n## Javascript\n`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should push', async (t) => {
|
||||||
|
await pushNewFiles([{ filename: 'README.md', data: '# title' }]);
|
||||||
|
t.true(writeFile.called);
|
||||||
|
t.true(pull.called);
|
||||||
|
t.true(add.called);
|
||||||
|
t.true(commit.called);
|
||||||
|
t.true(push.called);
|
||||||
|
});
|
Reference in New Issue
Block a user