Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 729d40d

Browse files
committedDec 25, 2024·
ci: Update typescript example
0 parents  commit 729d40d

39 files changed

+1667
-0
lines changed
 

‎.dockerignore

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# all start with .(dot), including directories and files
2+
.*
3+
CHANGELOG.md
4+
CODE_OF_CONDUCT.md
5+
compose.yml
6+
CONTRIBUTING.md
7+
Dockerfile
8+
LICENSE*
9+
README.md
10+
SECURITY.md

‎.editorconfig

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
root = true
2+
3+
[*]
4+
charset = utf-8
5+
end_of_line = lf
6+
indent_size = 4
7+
indent_style = space
8+
insert_final_newline = true
9+
max_line_length = 120
10+
tab_width = 4
11+
trim_trailing_whitespace = true

‎.github/ISSUE_TEMPLATE/bug_report.md

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
---
2+
name: Bug report
3+
about: Create a report to help us improve
4+
title: 'bug: '
5+
labels: bug
6+
assignees: ''
7+
8+
---
9+
10+
## Bug description
11+
12+
<!-- A clear and concise description of what the bug is. -->
13+
14+
- Would you like to work on a fix? [y/n]
15+
16+
## To Reproduce
17+
18+
Steps to reproduce the behavior:
19+
20+
1. ...
21+
2. ...
22+
3. ...
23+
4. ...
24+
25+
<!-- Make sure you are able to reproduce the bug in the main branch, too. -->
26+
27+
## Expected behavior
28+
29+
<!-- A clear and concise description of what you expected to happen. -->
30+
31+
## Screenshots
32+
33+
<!-- If applicable, add screenshots to help explain your problem. -->
34+
35+
## Environment
36+
37+
<!-- Please fill the following information. -->
38+
39+
- OS: [e.g. Ubuntu 20.04]
40+
- example_ts version: [e.g. 0.1.0]
41+
42+
## Additional context
43+
44+
<!-- Add any other context about the problem here. -->

‎.github/ISSUE_TEMPLATE/config.yml

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
blank_issues_enabled: true
+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
name: Feature request
3+
about: Suggest an idea for this project
4+
title: 'Feature Request: '
5+
labels: enhancement
6+
assignees: ''
7+
8+
---
9+
10+
## Motivations
11+
12+
<!--
13+
If your feature request is related to a problem, please describe it.
14+
-->
15+
16+
- Would you like to implement this feature? [y/n]
17+
18+
## Solution
19+
20+
<!-- Describe the solution you'd like. -->
21+
22+
## Alternatives
23+
24+
<!-- Describe any alternative solutions or features you've considered. -->
25+
26+
## Additional context
27+
28+
<!-- Add any other context or screenshots about the feature request here. -->

‎.github/PULL_REQUEST_TEMPLATE.md

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<!-- Please explain the changes you made -->
2+
3+
<!--
4+
Please make sure:
5+
- you have read the contributing guidelines:
6+
https://github.com/x-pt/example-ts/blob/main/docs/CONTRIBUTING.md
7+
- you have updated the changelog (if needed):
8+
https://github.com/x-pt/example-ts/blob/main/CHANGELOG.md
9+
-->

‎.github/configs/labeler.yml

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
version: 1
2+
3+
labels:
4+
# Type: Build-related changes
5+
- label: "@type/build"
6+
title: '^build(?:\(.+\))?\!?:'
7+
8+
# Type: CI-related changes
9+
- label: "@type/ci"
10+
title: '^ci(?:\(.+\))?\!?:'
11+
files:
12+
- '\.github/.+'
13+
14+
# Type: Documentation changes
15+
- label: "@type/docs"
16+
title: '^docs(?:\(.+\))?\!?:'
17+
files:
18+
- "docs/.+"
19+
- "**/*.md"
20+
21+
# Type: New feature
22+
- label: "@type/feature"
23+
title: '^feat(?:\(.+\))?\!?:'
24+
25+
# Type: Bug fix
26+
- label: "@type/fix"
27+
title: '^fix(?:\(.+\))?\!?:'
28+
29+
# Type: Improvements such as style changes, refactoring, or performance improvements
30+
- label: "@type/improve"
31+
title: '^(style|refactor|perf)(?:\(.+\))?\!?:'
32+
33+
# Type: Dependency changes
34+
- label: "@type/dependency"
35+
title: '^(chore|build)(?:\(deps\))?\!?:'
36+
37+
# Type: Test-related changes
38+
- label: "@type/test"
39+
title: '^test(?:\(.+\))?\!?:'
40+
files:
41+
- "tests/.+"
42+
- "spec/.+"
43+
44+
# Type: Security-related changes
45+
- label: "@type/security"
46+
title: '^security(?:\(.+\))?\!?:'
47+
files:
48+
- "**/security/.+"
49+
50+
# Issue Type Only: Feature Request
51+
- label: "Feature Request"
52+
type: issue
53+
title: "^Feature Request:"
54+
55+
# Issue Type Only: Documentation
56+
- label: "Documentation"
57+
type: issue
58+
title: "^.*(\b[Dd]ocumentation|doc(s)?\b).*"
59+
60+
# Issue Type Only: Bug Report
61+
- label: "Bug Report"
62+
type: issue
63+
title: "^.*(\b[Bb]ug|b(u)?g(s)?\b).*"

‎.github/dependabot.yml

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: "github-actions"
4+
directory: "/"
5+
# Check for updates every Monday
6+
schedule:
7+
interval: "weekly"
8+
open-pull-requests-limit: 10

‎.github/renovate.json

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
3+
"extends": [
4+
"config:base"
5+
]
6+
}

‎.github/workflows/cd.yml

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
name: CD # Continuous Deployment or Delivery
2+
3+
on:
4+
push:
5+
# e.g. 1.0.0, v2.0.0, v0.1.0, v0.2.0-alpha, v0.3.0+build-71edf32
6+
tags:
7+
- '[v]?[0-9]+\.[0-9]+\.[0-9]+.*'
8+
9+
jobs:
10+
dd:
11+
name: Deploy or Delivery
12+
runs-on: ubuntu-latest
13+
steps:
14+
- uses: actions/checkout@v4

‎.github/workflows/ci.yml

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
name: CI # Continuous Integration
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
workflow_dispatch:
9+
10+
11+
jobs:
12+
build-and-test:
13+
name: Node.js-${{ matrix.node }} on ${{ matrix.os }}
14+
15+
strategy:
16+
matrix:
17+
node: [ 20, 22 ]
18+
os:
19+
- ubuntu-latest
20+
- windows-latest
21+
- macos-latest
22+
23+
fail-fast: false
24+
25+
runs-on: ${{ matrix.os }}
26+
27+
steps:
28+
- uses: actions/checkout@v4
29+
- uses: pnpm/action-setup@v4
30+
with:
31+
version: 9
32+
33+
- name: Set up Node.js
34+
uses: actions/setup-node@v4
35+
with:
36+
node-version: ${{ matrix.node }}
37+
# cache: "pnpm"
38+
39+
- name: Build
40+
run: |
41+
pnpm i
42+
pnpm build
43+
44+
- name: Test
45+
run: pnpm test
46+

‎.github/workflows/docker.yml

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
name: Build and Push Docker Image
2+
3+
on:
4+
push:
5+
tags:
6+
- '^v[0-9]+\.[0-9]+\.[0-9]+.*$'
7+
8+
jobs:
9+
docker:
10+
runs-on: ubuntu-latest
11+
steps:
12+
-
13+
name: Checkout
14+
uses: actions/checkout@v4
15+
-
16+
name: Acquire tag name
17+
run: echo "RELEASE_VERSION=${GITHUB_REF_NAME#refs/*/}" >> $GITHUB_ENV
18+
-
19+
name: Set up QEMU
20+
uses: docker/setup-qemu-action@v3
21+
-
22+
name: Set up Docker Buildx
23+
uses: docker/setup-buildx-action@v3
24+
-
25+
name: Login to GitHub Container Registry
26+
uses: docker/login-action@v3
27+
with:
28+
registry: ghcr.io
29+
username: ${{ github.repository_owner }}
30+
password: ${{ secrets.GITHUB_TOKEN }}
31+
-
32+
name: Build and Export to Docker
33+
uses: docker/build-push-action@v6
34+
with:
35+
context: .
36+
load: true
37+
tags: |
38+
ghcr.io/x-pt/example-ts:latest
39+
ghcr.io/x-pt/example-ts:${GITHUB_REF_NAME:1}
40+
-
41+
name: Test it before Push
42+
run: |
43+
docker run --rm ghcr.io/x-pt/example-ts:latest
44+
docker run --rm ghcr.io/x-pt/example-ts:${GITHUB_REF_NAME:1}
45+
-
46+
name: Build and Push
47+
uses: docker/build-push-action@v6
48+
with:
49+
context: .
50+
platforms: linux/amd64,linux/arm64
51+
push: true
52+
tags: |
53+
ghcr.io/x-pt/example-ts:latest
54+
ghcr.io/x-pt/example-ts:${GITHUB_REF_NAME:1}

‎.github/workflows/labeler.yml

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
name: Labeler
2+
3+
on:
4+
- pull_request
5+
- issues
6+
7+
jobs:
8+
labeler:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: srvaroa/labeler@master
12+
with:
13+
config_path: .github/configs/labeler.yml
14+
env:
15+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

‎.github/workflows/release.yml

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
name: Example Ts Release
2+
3+
on:
4+
push:
5+
tags:
6+
- "v*.*.*"
7+
8+
jobs:
9+
changelog:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- name: Checkout Code
13+
uses: actions/checkout@v4
14+
with:
15+
fetch-depth: 0
16+
17+
- name: Get Tag Version
18+
id: tag_version
19+
run: echo "CURRENT_TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
20+
21+
- name: Generate Full Changelog
22+
uses: orhun/git-cliff-action@v4
23+
with:
24+
config: cliff.toml
25+
args: --verbose
26+
env:
27+
OUTPUT: CHANGELOG.md
28+
GITHUB_REPO: ${{ github.repository }}
29+
30+
- name: Commit Changelog
31+
run: |
32+
git config user.name 'github-actions[bot]'
33+
git config user.email 'github-actions[bot]@users.noreply.github.com'
34+
set +e
35+
git switch main
36+
git add CHANGELOG.md
37+
git commit -m "chore(release-bot): prepare for release notes on ${CURRENT_TAG}"
38+
git push
39+
40+
release:
41+
runs-on: ubuntu-latest
42+
steps:
43+
- name: Checkout Code
44+
uses: actions/checkout@v4
45+
with:
46+
fetch-depth: 0
47+
48+
- name: Generate Latest Release Notes
49+
id: latest_release_notes
50+
uses: orhun/git-cliff-action@v4
51+
with:
52+
config: cliff.toml
53+
args: --latest --strip all
54+
env:
55+
OUTPUT: CHANGELOG.txt
56+
57+
- name: Create GitHub Release
58+
uses: softprops/action-gh-release@v2
59+
with:
60+
body_path: CHANGELOG.txt

‎.gitignore

+134
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
.vscode
2+
.idea
3+
.DS_Store
4+
lib
5+
6+
### Node template
7+
# Logs
8+
logs
9+
*.log
10+
npm-debug.log*
11+
yarn-debug.log*
12+
yarn-error.log*
13+
lerna-debug.log*
14+
.pnpm-debug.log*
15+
16+
# Diagnostic reports (https://nodejs.org/api/report.html)
17+
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
18+
19+
# Runtime data
20+
pids
21+
*.pid
22+
*.seed
23+
*.pid.lock
24+
25+
# Directory for instrumented libs generated by jscoverage/JSCover
26+
lib-cov
27+
28+
# Coverage directory used by tools like istanbul
29+
coverage
30+
*.lcov
31+
32+
# nyc test coverage
33+
.nyc_output
34+
35+
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
36+
.grunt
37+
38+
# Bower dependency directory (https://bower.io/)
39+
bower_components
40+
41+
# node-waf configuration
42+
.lock-wscript
43+
44+
# Compiled binary addons (https://nodejs.org/api/addons.html)
45+
build/Release
46+
47+
# Dependency directories
48+
node_modules/
49+
jspm_packages/
50+
51+
# Snowpack dependency directory (https://snowpack.dev/)
52+
web_modules/
53+
54+
# TypeScript cache
55+
*.tsbuildinfo
56+
57+
# Optional npm cache directory
58+
.npm
59+
60+
# Optional eslint cache
61+
.eslintcache
62+
63+
# Optional stylelint cache
64+
.stylelintcache
65+
66+
# Microbundle cache
67+
.rpt2_cache/
68+
.rts2_cache_cjs/
69+
.rts2_cache_es/
70+
.rts2_cache_umd/
71+
72+
# Optional REPL history
73+
.node_repl_history
74+
75+
# Output of 'npm pack'
76+
*.tgz
77+
78+
# Yarn Integrity file
79+
.yarn-integrity
80+
81+
# dotenv environment variable files
82+
.env
83+
.env.development.local
84+
.env.test.local
85+
.env.production.local
86+
.env.local
87+
88+
# parcel-bundler cache (https://parceljs.org/)
89+
.cache
90+
.parcel-cache
91+
92+
# Next.js build output
93+
.next
94+
out
95+
96+
# Nuxt.js build / generate output
97+
.nuxt
98+
99+
# Gatsby files
100+
.cache/
101+
# Comment in the public line in if your project uses Gatsby and not Next.js
102+
# https://nextjs.org/blog/next-9-1#public-directory-support
103+
# public
104+
105+
# vuepress build output
106+
.vuepress/dist
107+
108+
# vuepress v2.x temp and cache directory
109+
.temp
110+
111+
# Docusaurus cache and generated files
112+
.docusaurus
113+
114+
# Serverless directories
115+
.serverless/
116+
117+
# FuseBox cache
118+
.fusebox/
119+
120+
# DynamoDB Local files
121+
.dynamodb/
122+
123+
# TernJS port file
124+
.tern-port
125+
126+
# Stores VSCode versions used for testing VSCode extensions
127+
.vscode-test
128+
129+
# yarn v2
130+
.yarn/cache
131+
.yarn/unplugged
132+
.yarn/build-state.yml
133+
.yarn/install-state.gz
134+
.pnp.*

‎.husky/commit-msg

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pnpx commitlint --edit $1

‎.husky/pre-commit

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pnpm test
2+
pnpx lint-staged

‎CHANGELOG.md

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [Unreleased]
9+
10+
### Added
11+
12+
- support some features
13+
14+
### Changed
15+
16+
- change some existed behaviors/logic
17+
18+
### Fixed
19+
20+
- fix some bugs

‎CODE_OF_CONDUCT.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Code of Conduct
2+
3+
This project adheres to the TypeScript Code of Conduct, which can be found [here](https://google.github.io/styleguide/tsguide.html).

‎CONTRIBUTING.md

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Contribution guidelines
2+
3+
First off, thank you for considering contributing to `example-ts`.
4+
5+
If your contribution is not straightforward, please first discuss the change you
6+
wish to make by creating a new issue before making the change.
7+
8+
## Reporting issues
9+
10+
Before reporting an issue on the
11+
[issue tracker](https://github.com/x-pt/example-ts/issues),
12+
please check that it has not already been reported by searching for some related
13+
keywords.
14+
15+
## Pull requests
16+
17+
Try to do one pull request per change.
18+
19+
### Updating the changelog
20+
21+
Update the changes you have made in
22+
[CHANGELOG](CHANGELOG.md)
23+
file under the **Unreleased** section.
24+
25+
Add the changes of your pull request to one of the following subsections,
26+
depending on the types of changes defined by
27+
[Keep a changelog](https://keepachangelog.com/en/1.0.0/):
28+
29+
- `Added` for new features.
30+
- `Changed` for changes in existing functionality.
31+
- `Deprecated` for soon-to-be removed features.
32+
- `Removed` for now removed features.
33+
- `Fixed` for any bug fixes.
34+
- `Security` in case of vulnerabilities.
35+
36+
If the required subsection does not exist yet under **Unreleased**, create it!
37+
38+
## Developing
39+
40+
### Set up
41+
42+
This is no different from other Python projects.
43+
44+
```shell
45+
git clone https://github.com/x-pt/example-ts
46+
cd example-ts
47+
make test
48+
```
49+
50+
### Useful Commands

‎Dockerfile

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Base image for building the application
2+
FROM node:20-bookworm AS builder
3+
4+
# Set working directory
5+
WORKDIR /app
6+
7+
# Copy package.json and package-lock.json to the container
8+
COPY package.json yarn.lock ./
9+
10+
# Install dependencies
11+
RUN yarn install
12+
13+
# Copy the rest of the application code
14+
COPY . .
15+
16+
# Build the application
17+
RUN yarn build
18+
19+
# Separate stage for validation (build and test)
20+
FROM builder AS validator
21+
22+
# Run tests as part of the validation
23+
RUN yarn test
24+
25+
# Final image for running the application
26+
FROM node:20-slim-bookworm
27+
28+
LABEL author="X Author Name"
29+
30+
# Set working directory
31+
WORKDIR /app
32+
33+
# Copy built application code and node_modules from builder
34+
COPY --from=builder /app/dist ./dist
35+
COPY --from=builder /app/node_modules ./node_modules
36+
COPY package.json yarn.lock ./
37+
38+
# Expose the port the app runs on
39+
EXPOSE 3000
40+
41+
# Define a health check
42+
HEALTHCHECK --start-period=30s CMD curl -f http://localhost:3000 || exit 1
43+
44+
# Start the application
45+
CMD ["node", "dist/index.js"]

‎LICENSE-APACHE

+176
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
Apache License
2+
Version 2.0, January 2004
3+
http://www.apache.org/licenses/
4+
5+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6+
7+
1. Definitions.
8+
9+
"License" shall mean the terms and conditions for use, reproduction,
10+
and distribution as defined by Sections 1 through 9 of this document.
11+
12+
"Licensor" shall mean the copyright owner or entity authorized by
13+
the copyright owner that is granting the License.
14+
15+
"Legal Entity" shall mean the union of the acting entity and all
16+
other entities that control, are controlled by, or are under common
17+
control with that entity. For the purposes of this definition,
18+
"control" means (i) the power, direct or indirect, to cause the
19+
direction or management of such entity, whether by contract or
20+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
21+
outstanding shares, or (iii) beneficial ownership of such entity.
22+
23+
"You" (or "Your") shall mean an individual or Legal Entity
24+
exercising permissions granted by this License.
25+
26+
"Source" form shall mean the preferred form for making modifications,
27+
including but not limited to software source code, documentation
28+
source, and configuration files.
29+
30+
"Object" form shall mean any form resulting from mechanical
31+
transformation or translation of a Source form, including but
32+
not limited to compiled object code, generated documentation,
33+
and conversions to other media types.
34+
35+
"Work" shall mean the work of authorship, whether in Source or
36+
Object form, made available under the License, as indicated by a
37+
copyright notice that is included in or attached to the work
38+
(an example is provided in the Appendix below).
39+
40+
"Derivative Works" shall mean any work, whether in Source or Object
41+
form, that is based on (or derived from) the Work and for which the
42+
editorial revisions, annotations, elaborations, or other modifications
43+
represent, as a whole, an original work of authorship. For the purposes
44+
of this License, Derivative Works shall not include works that remain
45+
separable from, or merely link (or bind by name) to the interfaces of,
46+
the Work and Derivative Works thereof.
47+
48+
"Contribution" shall mean any work of authorship, including
49+
the original version of the Work and any modifications or additions
50+
to that Work or Derivative Works thereof, that is intentionally
51+
submitted to Licensor for inclusion in the Work by the copyright owner
52+
or by an individual or Legal Entity authorized to submit on behalf of
53+
the copyright owner. For the purposes of this definition, "submitted"
54+
means any form of electronic, verbal, or written communication sent
55+
to the Licensor or its representatives, including but not limited to
56+
communication on electronic mailing lists, source code control systems,
57+
and issue tracking systems that are managed by, or on behalf of, the
58+
Licensor for the purpose of discussing and improving the Work, but
59+
excluding communication that is conspicuously marked or otherwise
60+
designated in writing by the copyright owner as "Not a Contribution."
61+
62+
"Contributor" shall mean Licensor and any individual or Legal Entity
63+
on behalf of whom a Contribution has been received by Licensor and
64+
subsequently incorporated within the Work.
65+
66+
2. Grant of Copyright License. Subject to the terms and conditions of
67+
this License, each Contributor hereby grants to You a perpetual,
68+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69+
copyright license to reproduce, prepare Derivative Works of,
70+
publicly display, publicly perform, sublicense, and distribute the
71+
Work and such Derivative Works in Source or Object form.
72+
73+
3. Grant of Patent License. Subject to the terms and conditions of
74+
this License, each Contributor hereby grants to You a perpetual,
75+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76+
(except as stated in this section) patent license to make, have made,
77+
use, offer to sell, sell, import, and otherwise transfer the Work,
78+
where such license applies only to those patent claims licensable
79+
by such Contributor that are necessarily infringed by their
80+
Contribution(s) alone or by combination of their Contribution(s)
81+
with the Work to which such Contribution(s) was submitted. If You
82+
institute patent litigation against any entity (including a
83+
cross-claim or counterclaim in a lawsuit) alleging that the Work
84+
or a Contribution incorporated within the Work constitutes direct
85+
or contributory patent infringement, then any patent licenses
86+
granted to You under this License for that Work shall terminate
87+
as of the date such litigation is filed.
88+
89+
4. Redistribution. You may reproduce and distribute copies of the
90+
Work or Derivative Works thereof in any medium, with or without
91+
modifications, and in Source or Object form, provided that You
92+
meet the following conditions:
93+
94+
(a) You must give any other recipients of the Work or
95+
Derivative Works a copy of this License; and
96+
97+
(b) You must cause any modified files to carry prominent notices
98+
stating that You changed the files; and
99+
100+
(c) You must retain, in the Source form of any Derivative Works
101+
that You distribute, all copyright, patent, trademark, and
102+
attribution notices from the Source form of the Work,
103+
excluding those notices that do not pertain to any part of
104+
the Derivative Works; and
105+
106+
(d) If the Work includes a "NOTICE" text file as part of its
107+
distribution, then any Derivative Works that You distribute must
108+
include a readable copy of the attribution notices contained
109+
within such NOTICE file, excluding those notices that do not
110+
pertain to any part of the Derivative Works, in at least one
111+
of the following places: within a NOTICE text file distributed
112+
as part of the Derivative Works; within the Source form or
113+
documentation, if provided along with the Derivative Works; or,
114+
within a display generated by the Derivative Works, if and
115+
wherever such third-party notices normally appear. The contents
116+
of the NOTICE file are for informational purposes only and
117+
do not modify the License. You may add Your own attribution
118+
notices within Derivative Works that You distribute, alongside
119+
or as an addendum to the NOTICE text from the Work, provided
120+
that such additional attribution notices cannot be construed
121+
as modifying the License.
122+
123+
You may add Your own copyright statement to Your modifications and
124+
may provide additional or different license terms and conditions
125+
for use, reproduction, or distribution of Your modifications, or
126+
for any such Derivative Works as a whole, provided Your use,
127+
reproduction, and distribution of the Work otherwise complies with
128+
the conditions stated in this License.
129+
130+
5. Submission of Contributions. Unless You explicitly state otherwise,
131+
any Contribution intentionally submitted for inclusion in the Work
132+
by You to the Licensor shall be under the terms and conditions of
133+
this License, without any additional terms or conditions.
134+
Notwithstanding the above, nothing herein shall supersede or modify
135+
the terms of any separate license agreement you may have executed
136+
with Licensor regarding such Contributions.
137+
138+
6. Trademarks. This License does not grant permission to use the trade
139+
names, trademarks, service marks, or product names of the Licensor,
140+
except as required for reasonable and customary use in describing the
141+
origin of the Work and reproducing the content of the NOTICE file.
142+
143+
7. Disclaimer of Warranty. Unless required by applicable law or
144+
agreed to in writing, Licensor provides the Work (and each
145+
Contributor provides its Contributions) on an "AS IS" BASIS,
146+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147+
implied, including, without limitation, any warranties or conditions
148+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149+
PARTICULAR PURPOSE. You are solely responsible for determining the
150+
appropriateness of using or redistributing the Work and assume any
151+
risks associated with Your exercise of permissions under this License.
152+
153+
8. Limitation of Liability. In no event and under no legal theory,
154+
whether in tort (including negligence), contract, or otherwise,
155+
unless required by applicable law (such as deliberate and grossly
156+
negligent acts) or agreed to in writing, shall any Contributor be
157+
liable to You for damages, including any direct, indirect, special,
158+
incidental, or consequential damages of any character arising as a
159+
result of this License or out of the use or inability to use the
160+
Work (including but not limited to damages for loss of goodwill,
161+
work stoppage, computer failure or malfunction, or any and all
162+
other commercial damages or losses), even if such Contributor
163+
has been advised of the possibility of such damages.
164+
165+
9. Accepting Warranty or Additional Liability. While redistributing
166+
the Work or Derivative Works thereof, You may choose to offer,
167+
and charge a fee for, acceptance of support, warranty, indemnity,
168+
or other liability obligations and/or rights consistent with this
169+
License. However, in accepting such obligations, You may act only
170+
on Your own behalf and on Your sole responsibility, not on behalf
171+
of any other Contributor, and only if You agree to indemnify,
172+
defend, and hold each Contributor harmless for any liability
173+
incurred by, or claims asserted against, such Contributor by reason
174+
of your accepting any such warranty or additional liability.
175+
176+
END OF TERMS AND CONDITIONS

‎LICENSE-MIT

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2024 X Author Name
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

‎Makefile

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
.PHONY: help build test image clean
2+
.DEFAULT_GOAL := help
3+
4+
APP_NAME := example-ts
5+
6+
# Init the venv
7+
init:
8+
@pnpm i
9+
10+
# Build wheel
11+
build:
12+
@pnpm build
13+
14+
# Test
15+
test:
16+
@pnpm test
17+
18+
# Build image
19+
image:
20+
@docker image build -t $(APP_NAME) .
21+
22+
# Start a compose service
23+
compose-up:
24+
@docker compose -f ./compose.yml -p $(APP_NAME) up -d
25+
26+
# Shutdown a compose service
27+
compose-down:
28+
@docker compose -f ./compose.yml down
29+
30+
# Clean build artifacts
31+
clean:
32+
@rm -rf build dist *.egg-info htmlcov .coverage coverage.xml coverage.lcov coverage
33+
@docker compose -f ./compose.yml down -v
34+
@docker image rm -f $(APP_NAME)
35+
36+
# Show help
37+
help:
38+
@echo ""
39+
@echo "Usage:"
40+
@echo " make [target]"
41+
@echo ""
42+
@echo "Targets:"
43+
@awk '/^[a-zA-Z\-_0-9]+:/ \
44+
{ \
45+
helpMessage = match(lastLine, /^# (.*)/); \
46+
if (helpMessage) { \
47+
helpCommand = substr($$1, 0, index($$1, ":")-1); \
48+
helpMessage = substr(lastLine, RSTART + 2, RLENGTH); \
49+
printf "\033[36m%-22s\033[0m %s\n", helpCommand,helpMessage; \
50+
} \
51+
} { lastLine = $$0 }' $(MAKEFILE_LIST)

‎README.md

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# example-ts
2+
3+
[![CI](https://github.com/x-pt/example-ts/workflows/CI/badge.svg)](https://github.com/x-pt/example-ts/actions)
4+
[![Coverage Status](https://coveralls.io/repos/github/x-pt/example-ts/badge.svg?branch=main)](https://coveralls.io/github/x-pt/example-ts?branch=main)
5+
[![NPM version](https://badge.fury.io/js/example-ts.svg)](https://badge.fury.io/js/example-ts)
6+
[![Node Version](https://img.shields.io/node/v/example-ts.svg)](https://nodejs.org/en/)
7+
8+
## Table of Contents
9+
10+
- [Overview](#overview)
11+
- [Features](#features)
12+
- [Quick Start](#quick-start)
13+
- [Installation](#installation)
14+
- [Usage](#usage)
15+
- [Development](#development)
16+
- [Troubleshooting](#troubleshooting)
17+
- [Contributing](#contributing)
18+
- [License](#license)
19+
- [Changelog](#changelog)
20+
- [Contact](#contact)
21+
- [Acknowledgements](#acknowledgements)
22+
23+
## Overview
24+
25+
`example-ts` is a TypeScript project designed to [brief description of the project's main purpose or functionality]. This project aims to [explain the primary goals or problems it solves].
26+
27+
## Features
28+
29+
- **Feature 1**: [Detailed description of feature 1 and its benefits]
30+
- **Feature 2**: [Detailed description of feature 2 and its benefits]
31+
- **Feature 3**: [Detailed description of feature 3 and its benefits]
32+
- [Add more features as needed]
33+
34+
## Quick Start
35+
36+
```typescript
37+
import { doSomething } from 'example-ts';
38+
39+
// Example usage
40+
const result = doSomething();
41+
console.log(result);
42+
43+
// Add more examples showcasing key features
44+
```
45+
46+
## Installation
47+
48+
### Requirements
49+
- Node.js 20+
50+
- Dependencies:
51+
- [Dependency 1]: [version] - [brief description or purpose]
52+
- [Dependency 2]: [version] - [brief description or purpose]
53+
- [Add more dependencies as needed]
54+
55+
### User Installation
56+
Install `example-ts` using npm:
57+
58+
```bash
59+
npm install example-ts
60+
```
61+
62+
Or using yarn:
63+
64+
```bash
65+
yarn add example-ts
66+
```
67+
68+
## Usage
69+
70+
Here's a brief overview of basic usage:
71+
72+
```typescript
73+
import { doSomething } from 'example-ts';
74+
75+
// Example usage
76+
const result = doSomething();
77+
console.log(result);
78+
79+
```
80+
81+
For more detailed examples and explanations of key concepts, please refer to our comprehensive [Usage Guide](docs/usage.md).
82+
83+
## Development
84+
85+
For information on setting up the development environment, running tests, and contributing to the project, please refer to our [Development Guide](docs/development.md).
86+
87+
## Troubleshooting
88+
89+
If you encounter any issues while using `example-ts`, please check our [Troubleshooting Guide](docs/troubleshooting.md) for common problems and their solutions. If you can't find a solution to your problem, please [open an issue](https://github.com/x-pt/example-ts/issues) on our GitHub repository.
90+
91+
## Contributing
92+
93+
We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details on how to submit pull requests, report issues, or suggest improvements.
94+
95+
## License
96+
97+
This project is licensed under either of:
98+
99+
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
100+
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
101+
102+
at your option.
103+
104+
## Changelog
105+
106+
For a detailed history of changes to this project, please refer to our [CHANGELOG.md](CHANGELOG.md).
107+
108+
## Contact
109+
110+
[Provide information on how to contact the maintainers or where to ask questions]
111+
112+
## Acknowledgements
113+
114+
[Acknowledge contributors, inspirations, or resources used in the project]

‎SECURITY.md

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Security Policy
2+
3+
## Supported Versions
4+
5+
<!--
6+
Use this section to tell people about which versions of your project are
7+
currently being supported with security updates.
8+
-->
9+
10+
| Version | Supported |
11+
|---------|--------------------|
12+
| 5.1.x | :white_check_mark: |
13+
| 5.0.x | :x: |
14+
| 4.0.x | :white_check_mark: |
15+
| < 4.0 | :x: |
16+
17+
## Reporting a Vulnerability
18+
19+
<!--
20+
Use this section to tell people how to report a vulnerability.
21+
22+
Tell them where to go, how often they can expect to get an update on a
23+
reported vulnerability, what to expect if the vulnerability is accepted or
24+
declined, etc.
25+
-->

‎biome.json

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"$schema": "https://biomejs.dev/schemas/1.8.3/schema.json",
3+
"files": {
4+
"ignore": ["**/node_modules/**", "**/dist/**", "**/build/**", "**/coverage/**", "lib"]
5+
},
6+
"formatter": {
7+
"enabled": true,
8+
"formatWithErrors": false,
9+
"indentStyle": "space",
10+
"indentWidth": 4,
11+
"lineEnding": "lf",
12+
"lineWidth": 120,
13+
"attributePosition": "auto"
14+
},
15+
"linter": {
16+
"enabled": true,
17+
"rules": {
18+
"recommended": true
19+
}
20+
},
21+
"organizeImports": {
22+
"enabled": true
23+
}
24+
}

‎cliff.toml

+131
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
# git-cliff ~ default configuration file
2+
# https://git-cliff.org/docs/configuration
3+
#
4+
# Lines starting with "#" are comments.
5+
# Configuration options are organized into tables and keys.
6+
# See documentation for more information on available options.
7+
8+
[remote.github]
9+
owner = "x-pt"
10+
repo = "example-ts"
11+
12+
[changelog]
13+
# template for the changelog footer
14+
header = """
15+
# Changelog\n
16+
All notable changes to this project will be documented in this file.\n
17+
"""
18+
# template for the changelog body
19+
# https://keats.github.io/tera/docs/#introduction
20+
body = """
21+
{%- macro remote_url() -%}
22+
https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }}
23+
{%- endmacro -%}
24+
25+
{% macro print_commit(commit) -%}
26+
- {% if commit.scope %}*({{ commit.scope }})* {% endif %}\
27+
{% if commit.breaking %}[**breaking**] {% endif %}\
28+
{{ commit.message | upper_first }} - \
29+
([{{ commit.id | truncate(length=7, end="") }}]({{ self::remote_url() }}/commit/{{ commit.id }}))\
30+
{% endmacro -%}
31+
32+
{% if version %}\
33+
{% if previous.version %}\
34+
## [{{ version | trim_start_matches(pat="v") }}]\
35+
({{ self::remote_url() }}/compare/{{ previous.version }}..{{ version }}) - {{ timestamp | date(format="%Y-%m-%d") }}
36+
{% else %}\
37+
## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
38+
{% endif %}\
39+
{% else %}\
40+
## [unreleased]
41+
{% endif %}\
42+
43+
{% for group, commits in commits | group_by(attribute="group") %}
44+
### {{ group | striptags | trim | upper_first }}
45+
{% for commit in commits
46+
| filter(attribute="scope")
47+
| sort(attribute="scope") %}
48+
{{ self::print_commit(commit=commit) }}
49+
{%- endfor %}
50+
{% for commit in commits %}
51+
{%- if not commit.scope -%}
52+
{{ self::print_commit(commit=commit) }}
53+
{% endif -%}
54+
{% endfor -%}
55+
{% endfor -%}
56+
{%- if github -%}
57+
{% if github.contributors | filter(attribute="is_first_time", value=true) | length != 0 %}
58+
## New Contributors ❤️
59+
{% endif %}\
60+
{% for contributor in github.contributors | filter(attribute="is_first_time", value=true) %}
61+
* @{{ contributor.username }} made their first contribution
62+
{%- if contributor.pr_number %} in \
63+
[#{{ contributor.pr_number }}]({{ self::remote_url() }}/pull/{{ contributor.pr_number }}) \
64+
{%- endif %}
65+
{%- endfor -%}
66+
{%- endif %}
67+
68+
69+
"""
70+
71+
# template for the changelog footer
72+
footer = """
73+
<!-- generated by git-cliff -->
74+
"""
75+
# remove the leading and trailing s
76+
trim = true
77+
# postprocessors
78+
postprocessors = [
79+
{ pattern = '<REPO>', replace = "https://github.com/x-pt/example-ts" }, # replace repository URL
80+
]
81+
82+
[git]
83+
# parse the commits based on https://www.conventionalcommits.org
84+
conventional_commits = true
85+
# filter out the commits that are not conventional
86+
filter_unconventional = true
87+
# process each line of a commit as an individual commit
88+
split_commits = false
89+
# regex for preprocessing the commit messages
90+
commit_preprocessors = [
91+
# Replace issue numbers
92+
{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](<REPO>/issues/${2}))"},
93+
# Check spelling of the commit with https://github.com/crate-ci/typos
94+
# If the spelling is incorrect, it will be automatically fixed.
95+
#{ pattern = '.*', replace_command = 'typos --write-changes -' },
96+
]
97+
# regex for parsing and grouping commits
98+
commit_parsers = [
99+
{ message = "^feat", group = "<!-- 0 -->🚀 Features" },
100+
{ message = "^fix", group = "<!-- 1 -->🐛 Bug Fixes" },
101+
{ message = "^doc", group = "<!-- 3 -->📚 Documentation" },
102+
{ message = "^perf", group = "<!-- 4 -->⚡ Performance" },
103+
{ message = "^refactor", group = "<!-- 2 -->🚜 Refactor" },
104+
{ message = "^style", group = "<!-- 5 -->🎨 Styling" },
105+
{ message = "^test", group = "<!-- 6 -->🧪 Testing" },
106+
{ message = "^chore\\(release\\): prepare for", skip = true },
107+
{ message = "^chore\\(release-bot\\): prepare for", skip = true },
108+
{ message = "^chore: bump version to", skip = true },
109+
{ message = "^chore\\(deps.*\\)", skip = true },
110+
{ message = "^chore\\(pr\\)", skip = true },
111+
{ message = "^chore\\(pull\\)", skip = true },
112+
{ message = "^chore|^ci", group = "<!-- 7 -->⚙️ Miscellaneous Tasks" },
113+
{ body = ".*security", group = "<!-- 8 -->🛡️ Security" },
114+
{ message = "^revert", group = "<!-- 9 -->◀️ Revert" },
115+
]
116+
# protect breaking changes from being skipped due to matching a skipping commit_parser
117+
protect_breaking_commits = false
118+
# filter out the commits that are not matched by commit parsers
119+
filter_commits = false
120+
# regex for matching git tags
121+
# tag_pattern = "v[0-9].*"
122+
# regex for skipping tags
123+
# skip_tags = ""
124+
# regex for ignoring tags
125+
# ignore_tags = ""
126+
# sort the tags topologically
127+
topo_order = false
128+
# sort the commits inside sections by oldest/newest order
129+
sort_commits = "newest"
130+
# limit the number of commits included in the changelog.
131+
# limit_commits = 42

‎commitlint.config.ts

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import type { UserConfig } from "@commitlint/types";
2+
3+
const Configuration: UserConfig = {
4+
extends: ["@commitlint/config-conventional"],
5+
};
6+
7+
export default Configuration;

‎compose.yml

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
services:
2+
example-ts:
3+
build: .
4+
image: example-ts
5+
ports:
6+
- 8000:8000
7+
8+
networks:
9+
example-ts-net:
10+
name: example-ts-net
11+
ipam:
12+
config:
13+
- subnet: 172.16.238.0/24

‎docs/development.md

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# Development Guide for example-ts
2+
3+
Welcome to the development guide for `example-ts`! This document will guide you through setting up your development environment, running tests, building the project, and maintaining code quality.
4+
5+
## Table of Contents
6+
7+
- [Setting Up the Development Environment](#setting-up-the-development-environment)
8+
- [Prerequisites](#prerequisites)
9+
- [Installation Steps](#installation-steps)
10+
- [Running Tests](#running-tests)
11+
- [Building the Project](#building-the-project)
12+
- [Code Style and Linting](#code-style-and-linting)
13+
14+
## Setting Up the Development Environment
15+
16+
### Prerequisites
17+
18+
Before you begin, make sure you have the following installed on your system:
19+
20+
- **Node.js 20+**: Ensure you have the correct version of Node.js. You can check your Node.js version with:
21+
22+
```bash
23+
node --version
24+
```
25+
26+
- **npm or yarn**: A package manager is required to install dependencies. You can check if you have npm or yarn installed with:
27+
28+
```bash
29+
npm --version
30+
yarn --version
31+
```
32+
33+
### Installation Steps
34+
35+
1. **Clone the Repository**: Start by cloning the project repository to your local machine and navigate to the project directory:
36+
37+
```bash
38+
git clone https://github.com/x-pt/example-ts.git
39+
cd example-ts
40+
```
41+
42+
2. **Install Dependencies**: Use npm or yarn to install all necessary dependencies:
43+
44+
```bash
45+
npm install
46+
# or
47+
yarn install
48+
```
49+
50+
This step will also set up any pre-commit hooks defined in the project, ensuring your code adheres to the project’s coding standards.
51+
52+
## Running Tests
53+
54+
Running tests is crucial to ensure the stability of the project. To run all tests, use the following command:
55+
56+
```bash
57+
npm test
58+
# or
59+
yarn test
60+
```
61+
62+
This command will execute the test suite using `jest`, ensuring all components work as expected.
63+
64+
[You may include additional details on the testing framework, testing strategy, or how to add new tests.]
65+
66+
## Building the Project
67+
68+
To build the project and generate the compiled JavaScript files, use:
69+
70+
```bash
71+
npm run build
72+
# or
73+
yarn build
74+
```
75+
76+
This command will compile the TypeScript files into JavaScript and place them in the `dist` directory.
77+
78+
## Code Style and Linting
79+
80+
Maintaining consistent code style is essential. We use `biome` for linting and formatting. To check for any style issues and automatically fix them, run:
81+
82+
```bash
83+
npm run lint
84+
# or
85+
yarn lint
86+
```
87+
88+
This command will check the codebase for any style issues and ensure that all code follows the project's style guide.
89+
90+
---
91+
92+
By following this guide, you'll be well-prepared to contribute to `example-ts`. Thank you for helping maintain and improve this project!

‎docs/troubleshooting.md

Whitespace-only changes.

‎docs/usage.md

Whitespace-only changes.

‎jest.config.json

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"preset": "ts-jest",
3+
"testEnvironment": "node",
4+
"collectCoverage": true,
5+
"coverageReporters": ["lcov", "text-summary"],
6+
"collectCoverageFrom": ["src/**/*.ts"],
7+
"coveragePathIgnorePatterns": ["/node_modules/", "/tests/"],
8+
"testPathIgnorePatterns": ["/node_modules/"]
9+
}

‎package.json

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
{
2+
"name": "example-ts",
3+
"version": "0.0.1",
4+
"description": "A nice example project",
5+
"author": "X Author Name",
6+
"repository": {
7+
"type": "git",
8+
"url": "git+https://github.com/x-pt/example-ts"
9+
},
10+
"license": "MIT",
11+
"engines": {
12+
"node": ">=20"
13+
},
14+
"scripts": {
15+
"build": "tsc && ncc build lib/index.js",
16+
"test": "jest",
17+
"lint": "biome check --write --no-errors-on-unmatched --files-ignore-unknown=true",
18+
"prepare": "husky",
19+
"commit": "pnpx git-cz"
20+
},
21+
"dependencies": {
22+
"@actions/core": "^1.11.1",
23+
"@actions/github": "^6.0.0",
24+
"axios": "^1.7.7",
25+
"toml": "^3.0.0"
26+
},
27+
"devDependencies": {
28+
"@biomejs/biome": "^1.9.3",
29+
"@commitlint/cli": "^19.5.0",
30+
"@commitlint/config-conventional": "^19.5.0",
31+
"@commitlint/cz-commitlint": "^19.5.0",
32+
"@commitlint/types": "^19.5.0",
33+
"@types/jest": "^29.5.13",
34+
"@types/node": "^22.7.5",
35+
"@vercel/ncc": "^0.38.2",
36+
"husky": "^9.1.6",
37+
"inquirer": "^12.0.0",
38+
"jest": "^29.7.0",
39+
"lint-staged": "^15.2.10",
40+
"ts-jest": "^29.2.5",
41+
"typescript": "^5.6.3"
42+
},
43+
"lint-staged": {
44+
"*": ["biome check --write --no-errors-on-unmatched --files-ignore-unknown=true"]
45+
},
46+
"config": {
47+
"commitizen": {
48+
"path": "@commitlint/cz-commitlint"
49+
}
50+
}
51+
}

‎src/index.ts

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import * as fs from "node:fs/promises";
2+
import * as core from "@actions/core";
3+
import axios from "axios";
4+
import * as toml from "toml";
5+
6+
interface Config {
7+
input_text?: string;
8+
find_word?: string;
9+
replace_word?: string;
10+
number_list?: number[];
11+
input_file?: string;
12+
output_file?: string;
13+
append_text?: string;
14+
api_url?: string;
15+
}
16+
17+
async function checkAPIReachability(apiUrl: string): Promise<void> {
18+
try {
19+
const response = await axios.get(apiUrl, { timeout: 10000 });
20+
if (response.status < 200 || response.status >= 300) {
21+
core.warning(`API is not reachable, status code: ${response.status}`);
22+
} else {
23+
core.info(`API ${apiUrl} is reachable.`);
24+
}
25+
} catch (error) {
26+
core.warning(`Failed to make API request: ${error instanceof Error ? error.message : String(error)}`);
27+
}
28+
}
29+
30+
async function readAndAppendToFile(inputFile: string, outputFile: string, appendText: string): Promise<void> {
31+
try {
32+
const content = await fs.readFile(inputFile, "utf-8");
33+
const modifiedContent = `${content}\n${appendText}`;
34+
await fs.writeFile(outputFile, modifiedContent, { encoding: "utf-8" });
35+
core.info(`Appended text to file: ${outputFile}`);
36+
} catch (error) {
37+
throw new Error(`File operation failed: ${error instanceof Error ? error.message : String(error)}`);
38+
}
39+
}
40+
41+
function processText(
42+
text: string,
43+
findWord: string,
44+
replaceWord: string,
45+
): {
46+
processedText: string;
47+
wordCount: number;
48+
} {
49+
const processedText = text.replace(new RegExp(findWord, "g"), replaceWord);
50+
const wordCount = processedText.trim() === "" ? 0 : processedText.trim().split(/\s+/).length;
51+
return { processedText, wordCount };
52+
}
53+
54+
function calculateNumberStats(numbers: number[]): { sum: number; average: number } {
55+
const sum = numbers.reduce((acc, num) => acc + num, 0);
56+
const average = numbers.length > 0 ? sum / numbers.length : 0;
57+
return { sum, average };
58+
}
59+
60+
export async function run(): Promise<void> {
61+
try {
62+
const configPath = core.getInput("config_path") || ".github/configs/setup-custom-action-by-ts.toml";
63+
const configContent = await fs.readFile(configPath, "utf-8");
64+
const config: Config = toml.parse(configContent);
65+
66+
const {
67+
input_text = "",
68+
find_word = "",
69+
replace_word = "",
70+
number_list = [],
71+
input_file = "",
72+
output_file = "",
73+
append_text = "",
74+
api_url = "",
75+
} = config;
76+
77+
if (api_url) {
78+
await checkAPIReachability(api_url);
79+
}
80+
81+
if (input_file && output_file && append_text) {
82+
await readAndAppendToFile(input_file, output_file, append_text);
83+
}
84+
85+
const { processedText, wordCount } = processText(input_text, find_word, replace_word);
86+
const { sum, average } = calculateNumberStats(number_list);
87+
88+
core.setOutput("processed_text", processedText);
89+
core.setOutput("word_count", wordCount);
90+
core.setOutput("sum", sum);
91+
core.setOutput("average", average);
92+
} catch (error) {
93+
core.setFailed(`Action failed with error: ${error instanceof Error ? error.message : String(error)}`);
94+
}
95+
}
96+
97+
if (!process.env.JEST_WORKER_ID) {
98+
run().catch((error) => {
99+
console.error("Unhandled error:", error);
100+
process.exit(1);
101+
});
102+
}
+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`GitHub Action Configuration Handling should use default values when config is empty 1`] = `
4+
[
5+
[
6+
"processed_text",
7+
"",
8+
],
9+
[
10+
"word_count",
11+
0,
12+
],
13+
[
14+
"sum",
15+
0,
16+
],
17+
[
18+
"average",
19+
0,
20+
],
21+
]
22+
`;
23+
24+
exports[`GitHub Action Text Processing should handle empty input text 1`] = `
25+
[
26+
[
27+
"processed_text",
28+
"",
29+
],
30+
[
31+
"word_count",
32+
0,
33+
],
34+
[
35+
"sum",
36+
0,
37+
],
38+
[
39+
"average",
40+
0,
41+
],
42+
]
43+
`;
44+
45+
exports[`GitHub Action Text Processing should process text, count words, calculate sum and average correctly 1`] = `
46+
[
47+
[
48+
"processed_text",
49+
"Hi world! Hi!",
50+
],
51+
[
52+
"word_count",
53+
3,
54+
],
55+
[
56+
"sum",
57+
15,
58+
],
59+
[
60+
"average",
61+
3,
62+
],
63+
]
64+
`;

‎tests/index.test.ts

+151
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
import * as fs from "node:fs/promises";
2+
import * as core from "@actions/core";
3+
import axios from "axios";
4+
import { run } from "../src";
5+
6+
jest.mock("@actions/core");
7+
jest.mock("node:fs/promises");
8+
jest.mock("axios");
9+
10+
const mockConfig = `
11+
input_text = "Hello world! Hello!"
12+
find_word = "Hello"
13+
replace_word = "Hi"
14+
number_list = [1, 2, 3, 4, 5]
15+
input_file = "input.txt"
16+
output_file = "output.txt"
17+
append_text = "Goodbye!"
18+
api_url = "https://api.example.com/data"
19+
`;
20+
21+
describe("GitHub Action", () => {
22+
const mockGetInput = jest.spyOn(core, "getInput");
23+
const mockSetOutput = jest.spyOn(core, "setOutput");
24+
const mockSetFailed = jest.spyOn(core, "setFailed");
25+
const mockWarning = jest.spyOn(core, "warning");
26+
const mockInfo = jest.spyOn(core, "info");
27+
const mockReadFile = fs.readFile as jest.MockedFunction<typeof fs.readFile>;
28+
const mockWriteFile = fs.writeFile as jest.MockedFunction<typeof fs.writeFile>;
29+
const mockAxiosGet = axios.get as jest.MockedFunction<typeof axios.get>;
30+
31+
beforeAll(() => {
32+
mockGetInput.mockReturnValue(".github/configs/setup-custom-action-by-ts.toml");
33+
});
34+
35+
beforeEach(() => {
36+
jest.resetAllMocks();
37+
setupDefaultMocks();
38+
});
39+
40+
const setupDefaultMocks = () => {
41+
mockReadFile.mockResolvedValue(mockConfig);
42+
mockAxiosGet.mockResolvedValue({ status: 200, data: {} });
43+
};
44+
45+
const setupFileMocks = (inputContent: string) => {
46+
mockReadFile.mockImplementation((path) => {
47+
if (path === ".github/configs/setup-custom-action-by-ts.toml") {
48+
return Promise.resolve(mockConfig);
49+
}
50+
if (path === "input.txt") {
51+
return Promise.resolve(inputContent);
52+
}
53+
return Promise.reject(new Error("Unexpected file read"));
54+
});
55+
};
56+
57+
describe("Text Processing", () => {
58+
it("should process text, count words, calculate sum and average correctly", async () => {
59+
await run();
60+
61+
expect(mockSetOutput.mock.calls).toMatchSnapshot();
62+
});
63+
64+
it("should handle empty input text", async () => {
65+
mockReadFile.mockResolvedValue(`
66+
input_text = ""
67+
number_list = []
68+
`);
69+
70+
await run();
71+
72+
expect(mockSetOutput.mock.calls).toMatchSnapshot();
73+
});
74+
});
75+
76+
describe("File Operations", () => {
77+
it("should read from the input file and append text to the output file correctly", async () => {
78+
setupFileMocks("Original file content.");
79+
80+
await run();
81+
82+
expect(mockReadFile).toHaveBeenCalledWith("input.txt", "utf-8");
83+
expect(mockWriteFile).toHaveBeenCalledWith("output.txt", "Original file content.\nGoodbye!", {
84+
encoding: "utf-8",
85+
});
86+
expect(mockInfo).toHaveBeenCalledWith("Appended text to file: output.txt");
87+
});
88+
89+
it("should handle missing input file gracefully", async () => {
90+
mockReadFile.mockRejectedValue(new Error("File not found"));
91+
92+
await run();
93+
94+
expect(mockSetFailed).toHaveBeenCalledWith("Action failed with error: File not found");
95+
});
96+
97+
it("should handle file write errors gracefully", async () => {
98+
setupFileMocks("Original file content.");
99+
mockWriteFile.mockRejectedValue(new Error("Failed to write to output file"));
100+
101+
await run();
102+
103+
expect(mockSetFailed).toHaveBeenCalledWith(
104+
"Action failed with error: File operation failed: Failed to write to output file",
105+
);
106+
});
107+
});
108+
109+
describe("API Operations", () => {
110+
it("should check API reachability correctly", async () => {
111+
await run();
112+
113+
expect(mockAxiosGet).toHaveBeenCalledWith("https://api.example.com/data", { timeout: 10000 });
114+
expect(mockInfo).toHaveBeenCalledWith("API https://api.example.com/data is reachable.");
115+
});
116+
117+
it("should handle API request failures gracefully", async () => {
118+
mockAxiosGet.mockRejectedValue(new Error("API error"));
119+
120+
await run();
121+
122+
expect(mockWarning).toHaveBeenCalledWith("Failed to make API request: API error");
123+
});
124+
125+
it("should warn on bad API status", async () => {
126+
mockAxiosGet.mockResolvedValue({ status: 500, data: {} });
127+
128+
await run();
129+
130+
expect(mockWarning).toHaveBeenCalledWith("API is not reachable, status code: 500");
131+
});
132+
});
133+
134+
describe("Configuration Handling", () => {
135+
it("should use default values when config is empty", async () => {
136+
mockReadFile.mockResolvedValue("");
137+
138+
await run();
139+
140+
expect(mockSetOutput.mock.calls).toMatchSnapshot();
141+
});
142+
143+
it("should handle missing configuration file gracefully", async () => {
144+
mockReadFile.mockRejectedValue(new Error("Config file not found"));
145+
146+
await run();
147+
148+
expect(mockSetFailed).toHaveBeenCalledWith("Action failed with error: Config file not found");
149+
});
150+
});
151+
});

‎tsconfig.json

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"compilerOptions": {
3+
"target": "ES2023",
4+
"module": "commonjs",
5+
"outDir": "lib",
6+
"esModuleInterop": true,
7+
"forceConsistentCasingInFileNames": true,
8+
"strict": true,
9+
"skipLibCheck": true
10+
},
11+
"exclude": ["node_modules", "**/*.test.ts", "commitlint.config.ts"]
12+
}

0 commit comments

Comments
 (0)
Please sign in to comment.