feat: initial version
This commit is contained in:
commit
2fc1d51eb6
37 changed files with 66401 additions and 0 deletions
41
.devcontainer/devcontainer.json
Normal file
41
.devcontainer/devcontainer.json
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
{
|
||||||
|
"name": "GitHub Actions (TypeScript)",
|
||||||
|
"image": "mcr.microsoft.com/devcontainers/typescript-node:20",
|
||||||
|
"postCreateCommand": "npm install",
|
||||||
|
"customizations": {
|
||||||
|
"codespaces": {
|
||||||
|
"openFiles": ["README.md"]
|
||||||
|
},
|
||||||
|
"vscode": {
|
||||||
|
"extensions": [
|
||||||
|
"bierner.markdown-preview-github-styles",
|
||||||
|
"davidanson.vscode-markdownlint",
|
||||||
|
"dbaeumer.vscode-eslint",
|
||||||
|
"esbenp.prettier-vscode",
|
||||||
|
"github.copilot",
|
||||||
|
"github.copilot-chat",
|
||||||
|
"github.vscode-github-actions",
|
||||||
|
"github.vscode-pull-request-github",
|
||||||
|
"me-dutour-mathieu.vscode-github-actions",
|
||||||
|
"redhat.vscode-yaml",
|
||||||
|
"rvest.vs-code-prettier-eslint",
|
||||||
|
"yzhang.markdown-all-in-one"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
|
"editor.tabSize": 2,
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"markdown.extension.list.indentationSize": "adaptive",
|
||||||
|
"markdown.extension.italic.indicator": "_",
|
||||||
|
"markdown.extension.orderedList.marker": "one"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"remoteEnv": {
|
||||||
|
"GITHUB_TOKEN": "${localEnv:GITHUB_TOKEN}"
|
||||||
|
},
|
||||||
|
"features": {
|
||||||
|
"ghcr.io/devcontainers/features/github-cli:1": {},
|
||||||
|
"ghcr.io/devcontainers-contrib/features/prettier:1": {}
|
||||||
|
}
|
||||||
|
}
|
4
.eslintignore
Normal file
4
.eslintignore
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
lib/
|
||||||
|
dist/
|
||||||
|
node_modules/
|
||||||
|
coverage/
|
72
.forgejo/workflows/check-dist.yml
Normal file
72
.forgejo/workflows/check-dist.yml
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
# In TypeScript actions, `dist/` is a special directory. When you reference
|
||||||
|
# an action with the `uses:` property, `dist/index.js` is the code that will be
|
||||||
|
# run. For this project, the `dist/index.js` file is transpiled from other
|
||||||
|
# source files. This workflow ensures the `dist/` directory contains the
|
||||||
|
# expected transpiled code.
|
||||||
|
#
|
||||||
|
# If this workflow is run from a feature branch, it will act as an additional CI
|
||||||
|
# check and fail if the checked-in `dist/` directory does not match what is
|
||||||
|
# expected from the build.
|
||||||
|
name: Check Transpiled JavaScript
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check-dist:
|
||||||
|
name: Check dist/
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
id: checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
id: setup-node
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version-file: .node-version
|
||||||
|
cache: npm
|
||||||
|
|
||||||
|
- name: Install Dependencies
|
||||||
|
id: install
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Build dist/ Directory
|
||||||
|
id: build
|
||||||
|
run: npm run bundle
|
||||||
|
|
||||||
|
# This will fail the workflow if the `dist/` directory is different than
|
||||||
|
# expected.
|
||||||
|
- name: Compare Directories
|
||||||
|
id: diff
|
||||||
|
run: |
|
||||||
|
if [ ! -d dist/ ]; then
|
||||||
|
echo "Expected dist/ directory does not exist. See status below:"
|
||||||
|
ls -la ./
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ "$(git diff --ignore-space-at-eol --text dist/ | wc -l)" -gt "0" ]; then
|
||||||
|
echo "Detected uncommitted changes after build. See status below:"
|
||||||
|
git diff --ignore-space-at-eol --text dist/
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If `dist/` was different than expected, upload the expected version as a
|
||||||
|
# workflow artifact.
|
||||||
|
- if: ${{ failure() && steps.diff.outcome == 'failure' }}
|
||||||
|
name: Upload Artifact
|
||||||
|
id: upload
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: dist
|
||||||
|
path: dist/
|
64
.forgejo/workflows/ci.yml
Normal file
64
.forgejo/workflows/ci.yml
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
name: Continuous Integration
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test-typescript:
|
||||||
|
name: TypeScript Tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
id: checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
id: setup-node
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version-file: .node-version
|
||||||
|
cache: npm
|
||||||
|
|
||||||
|
- name: Install Dependencies
|
||||||
|
id: npm-ci
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Check Format
|
||||||
|
id: npm-format-check
|
||||||
|
run: npm run format:check
|
||||||
|
|
||||||
|
- name: Lint
|
||||||
|
id: npm-lint
|
||||||
|
run: npm run lint
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
id: npm-ci-test
|
||||||
|
run: npm run ci-test
|
||||||
|
|
||||||
|
test-action:
|
||||||
|
name: GitHub Actions Test
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
id: checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Test Local Action
|
||||||
|
id: test-action
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
milliseconds: 2000
|
||||||
|
|
||||||
|
- name: Print Output
|
||||||
|
id: output
|
||||||
|
run: echo "${{ steps.test-action.outputs.time }}"
|
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
* text=auto eol=lf
|
||||||
|
dist/** filter=lfs diff=lfs merge=lfs -text
|
103
.gitignore
vendored
Normal file
103
.gitignore
vendored
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
# Dependency directory
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
# Rest pulled from https://github.com/github/gitignore/blob/master/Node.gitignore
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# TypeScript v1 declaration files
|
||||||
|
typings/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variables file
|
||||||
|
.env
|
||||||
|
.env.test
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
.cache
|
||||||
|
|
||||||
|
# next.js build output
|
||||||
|
.next
|
||||||
|
|
||||||
|
# nuxt.js build output
|
||||||
|
.nuxt
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# OS metadata
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Ignore built ts files
|
||||||
|
__tests__/runner/*
|
||||||
|
|
||||||
|
# IDE files
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
*.code-workspace
|
1
.node-version
Normal file
1
.node-version
Normal file
|
@ -0,0 +1 @@
|
||||||
|
20.6.0
|
3
.prettierignore
Normal file
3
.prettierignore
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
dist/
|
||||||
|
node_modules/
|
||||||
|
coverage/
|
16
.prettierrc.json
Normal file
16
.prettierrc.json
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"printWidth": 80,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"useTabs": false,
|
||||||
|
"semi": false,
|
||||||
|
"singleQuote": true,
|
||||||
|
"quoteProps": "as-needed",
|
||||||
|
"jsxSingleQuote": false,
|
||||||
|
"trailingComma": "none",
|
||||||
|
"bracketSpacing": true,
|
||||||
|
"bracketSameLine": true,
|
||||||
|
"arrowParens": "avoid",
|
||||||
|
"proseWrap": "always",
|
||||||
|
"htmlWhitespaceSensitivity": "css",
|
||||||
|
"endOfLine": "lf"
|
||||||
|
}
|
9
LICENSE
Normal file
9
LICENSE
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024 prskr
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
1
README.md
Normal file
1
README.md
Normal file
|
@ -0,0 +1 @@
|
||||||
|
# setup-hugo
|
39
__tests__/asset-lookup.test.ts
Normal file
39
__tests__/asset-lookup.test.ts
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import { OctokitReleaseLookup } from '../src/asset-lookup'
|
||||||
|
import { Platform } from '../src/os'
|
||||||
|
import { HugoReleaseTransformer } from '../src/hugo'
|
||||||
|
import { DartSass, Hugo } from '../src/constants'
|
||||||
|
import { DartSassReleaseTransformer } from '../src/dart-sass'
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.resetModules()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Asset lookup', () => {
|
||||||
|
test('Hugo: should return valid version', async () => {
|
||||||
|
const octoVersionDetermination = new OctokitReleaseLookup()
|
||||||
|
|
||||||
|
const release = await octoVersionDetermination.getRelease(
|
||||||
|
Hugo.Org,
|
||||||
|
Hugo.Repo,
|
||||||
|
'',
|
||||||
|
HugoReleaseTransformer
|
||||||
|
)
|
||||||
|
expect(release.tag_name).toMatch(new RegExp('\\d+.\\d+.\\d+'))
|
||||||
|
const archiveUrl = release.assetUrl(new Platform(), false)
|
||||||
|
expect(archiveUrl).not.toBe(undefined)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Dart-Sass: should return valid version', async () => {
|
||||||
|
const octoVersionDetermination = new OctokitReleaseLookup()
|
||||||
|
|
||||||
|
const release = await octoVersionDetermination.getRelease(
|
||||||
|
DartSass.Org,
|
||||||
|
DartSass.Repo,
|
||||||
|
'',
|
||||||
|
DartSassReleaseTransformer
|
||||||
|
)
|
||||||
|
expect(release.tag_name).toMatch(new RegExp('\\d+.\\d+.\\d+'))
|
||||||
|
const archiveUrl = release.assetUrl(new Platform())
|
||||||
|
expect(archiveUrl).not.toBe(undefined)
|
||||||
|
})
|
||||||
|
})
|
17
__tests__/index.test.ts
Normal file
17
__tests__/index.test.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
/**
|
||||||
|
* Unit tests for the action's entrypoint, src/index.ts
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as main from '../src/main'
|
||||||
|
|
||||||
|
// Mock the action's entrypoint
|
||||||
|
const runMock = jest.spyOn(main, 'run').mockImplementation()
|
||||||
|
|
||||||
|
describe('index', () => {
|
||||||
|
it('calls run when imported', async () => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||||
|
require('../src/index.ts')
|
||||||
|
|
||||||
|
expect(runMock).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
})
|
23
__tests__/os.test.ts
Normal file
23
__tests__/os.test.ts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import { Platform } from '../src/os'
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.resetModules()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Platform', () => {
|
||||||
|
test('getHomeDir - should return non-empty string', () => {
|
||||||
|
const homeDir = new Platform('linux', undefined, {
|
||||||
|
HOME: '/home/prskr'
|
||||||
|
}).getHomeDir()
|
||||||
|
|
||||||
|
expect(homeDir).toBe('/home/prskr')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('getHomeDir - return USERPROFILE for win32 platform', () => {
|
||||||
|
const homeDir = new Platform('win32', undefined, {
|
||||||
|
USERPROFILE: 'C:\\Users\\prskr'
|
||||||
|
}).getHomeDir()
|
||||||
|
|
||||||
|
expect(homeDir).toBe('C:\\Users\\prskr')
|
||||||
|
})
|
||||||
|
})
|
31
action.yml
Normal file
31
action.yml
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
name: 'Hello World'
|
||||||
|
description: 'Greet someone and record the time'
|
||||||
|
inputs:
|
||||||
|
hugo-version:
|
||||||
|
description:
|
||||||
|
'The Hugo version to download.ts (if necessary) and use. Example: 0.58.2'
|
||||||
|
required: false
|
||||||
|
default: 'latest'
|
||||||
|
extended:
|
||||||
|
description:
|
||||||
|
'Download (if necessary) and use Hugo extended version. Example: true'
|
||||||
|
required: false
|
||||||
|
default: 'false'
|
||||||
|
dart-sass:
|
||||||
|
description: 'Download (if necessary) dart-sass'
|
||||||
|
required: false
|
||||||
|
default: 'false'
|
||||||
|
dart-sass-version:
|
||||||
|
description:
|
||||||
|
'The dart-sass version to download.ts (if necessary) and use. Example:
|
||||||
|
1.76.0'
|
||||||
|
required: false
|
||||||
|
default: ''
|
||||||
|
github-token:
|
||||||
|
description:
|
||||||
|
'Used to avoid rate limits when interacting with the Github API'
|
||||||
|
required: false
|
||||||
|
default: ''
|
||||||
|
runs:
|
||||||
|
using: 'node20'
|
||||||
|
main: 'dist/index.js'
|
BIN
dist/index.js
(Stored with Git LFS)
vendored
Normal file
BIN
dist/index.js
(Stored with Git LFS)
vendored
Normal file
Binary file not shown.
BIN
dist/index.js.map
(Stored with Git LFS)
vendored
Normal file
BIN
dist/index.js.map
(Stored with Git LFS)
vendored
Normal file
Binary file not shown.
BIN
dist/licenses.txt
(Stored with Git LFS)
vendored
Normal file
BIN
dist/licenses.txt
(Stored with Git LFS)
vendored
Normal file
Binary file not shown.
BIN
dist/package.json
(Stored with Git LFS)
vendored
Normal file
BIN
dist/package.json
(Stored with Git LFS)
vendored
Normal file
Binary file not shown.
BIN
dist/sourcemap-register.cjs
(Stored with Git LFS)
vendored
Normal file
BIN
dist/sourcemap-register.cjs
(Stored with Git LFS)
vendored
Normal file
Binary file not shown.
53267
lib/index.js
Normal file
53267
lib/index.js
Normal file
File diff suppressed because one or more lines are too long
1
lib/index.js.map
Normal file
1
lib/index.js.map
Normal file
File diff suppressed because one or more lines are too long
1686
lib/licenses.txt
Normal file
1686
lib/licenses.txt
Normal file
File diff suppressed because it is too large
Load diff
3
lib/package.json
Normal file
3
lib/package.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"type": "module"
|
||||||
|
}
|
2349
lib/sourcemap-register.cjs
Normal file
2349
lib/sourcemap-register.cjs
Normal file
File diff suppressed because it is too large
Load diff
8118
package-lock.json
generated
Normal file
8118
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
93
package.json
Normal file
93
package.json
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
{
|
||||||
|
"name": "setup-hugo",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"private": true,
|
||||||
|
"keywords": [],
|
||||||
|
"author": "prskr",
|
||||||
|
"license": "MIT",
|
||||||
|
"type": "module",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
},
|
||||||
|
"exports": {
|
||||||
|
".": "./dist/index.js"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"bundle": "npm run format:write && npm run package",
|
||||||
|
"ci-test": "npx jest",
|
||||||
|
"coverage": "npx make-coverage-badge --output-path ./badges/coverage.svg",
|
||||||
|
"format:write": "npx prettier --write .",
|
||||||
|
"format:check": "npx prettier --check .",
|
||||||
|
"lint": "npx eslint . -c ./.github/linters/.eslintrc.yml",
|
||||||
|
"package": "npx ncc build src/index.ts -o dist --source-map --license licenses.txt",
|
||||||
|
"package:watch": "npm run package -- --watch",
|
||||||
|
"test": "npx jest",
|
||||||
|
"all": "npm run format:write && npm run lint && npm run test && npm run coverage && npm run package"
|
||||||
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"{src,__tests__}/**/*.ts": [
|
||||||
|
"prettier --check",
|
||||||
|
"eslint"
|
||||||
|
],
|
||||||
|
"README.md": [
|
||||||
|
"npx doctoc@2.1.0 --github"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"jest": {
|
||||||
|
"preset": "ts-jest",
|
||||||
|
"verbose": true,
|
||||||
|
"clearMocks": true,
|
||||||
|
"testEnvironment": "node",
|
||||||
|
"moduleFileExtensions": [
|
||||||
|
"js",
|
||||||
|
"ts"
|
||||||
|
],
|
||||||
|
"testMatch": [
|
||||||
|
"**/*.test.ts"
|
||||||
|
],
|
||||||
|
"testPathIgnorePatterns": [
|
||||||
|
"/node_modules/",
|
||||||
|
"/dist/"
|
||||||
|
],
|
||||||
|
"transform": {
|
||||||
|
"^.+\\.ts$": "ts-jest"
|
||||||
|
},
|
||||||
|
"coverageReporters": [
|
||||||
|
"json-summary",
|
||||||
|
"text",
|
||||||
|
"lcov"
|
||||||
|
],
|
||||||
|
"collectCoverage": true,
|
||||||
|
"collectCoverageFrom": [
|
||||||
|
"./src/**"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@actions/core": "^1.10.1",
|
||||||
|
"@actions/exec": "^1.1.1",
|
||||||
|
"@actions/io": "^1.1.3",
|
||||||
|
"@actions/tool-cache": "^2.0.1",
|
||||||
|
"octokit": "^3.2.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@jest/globals": "^29.7.0",
|
||||||
|
"@types/jest": "^29.5.12",
|
||||||
|
"@types/node": "^20.12.7",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^7.7.1",
|
||||||
|
"@typescript-eslint/parser": "^7.7.1",
|
||||||
|
"@vercel/ncc": "^0.38.1",
|
||||||
|
"eslint": "^8.57.0",
|
||||||
|
"eslint-plugin-github": "^4.10.2",
|
||||||
|
"eslint-plugin-jest": "^28.3.0",
|
||||||
|
"eslint-plugin-jsonc": "^2.15.1",
|
||||||
|
"eslint-plugin-prettier": "^5.1.3",
|
||||||
|
"jest": "^29.7.0",
|
||||||
|
"make-coverage-badge": "^1.2.0",
|
||||||
|
"nock": "^13.0.10",
|
||||||
|
"prettier": "^3.2.5",
|
||||||
|
"prettier-eslint": "^16.3.0",
|
||||||
|
"ts-jest": "^29.1.2",
|
||||||
|
"typescript": "^5.4.5"
|
||||||
|
}
|
||||||
|
}
|
59
script/release
Normal file
59
script/release
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# About:
|
||||||
|
#
|
||||||
|
# This is a helper script to tag and push a new release. GitHub Actions use
|
||||||
|
# release tags to allow users to select a specific version of the action to use.
|
||||||
|
#
|
||||||
|
# See: https://github.com/actions/typescript-action#publishing-a-new-release
|
||||||
|
#
|
||||||
|
# This script will do the following:
|
||||||
|
#
|
||||||
|
# 1. Get the latest release tag
|
||||||
|
# 2. Prompt the user for a new release tag
|
||||||
|
# 3. Tag the new release
|
||||||
|
# 4. Push the new tag to the remote
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
#
|
||||||
|
# script/release
|
||||||
|
|
||||||
|
# Terminal colors
|
||||||
|
OFF='\033[0m'
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
|
||||||
|
# Get the latest release tag
|
||||||
|
latest_tag=$(git describe --tags "$(git rev-list --tags --max-count=1)")
|
||||||
|
|
||||||
|
if [[ -z "$latest_tag" ]]; then
|
||||||
|
# There are no existing release tags
|
||||||
|
echo -e "No tags found (yet) - Continue to create and push your first tag"
|
||||||
|
latest_tag="[unknown]"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Display the latest release tag
|
||||||
|
echo -e "The latest release tag is: ${BLUE}${latest_tag}${OFF}"
|
||||||
|
|
||||||
|
# Prompt the user for the new release tag
|
||||||
|
read -r -p 'Enter a new release tag (vX.X.X format): ' new_tag
|
||||||
|
|
||||||
|
# Validate the new release tag
|
||||||
|
tag_regex='v[0-9]+\.[0-9]+\.[0-9]+$'
|
||||||
|
if echo "$new_tag" | grep -q -E "$tag_regex"; then
|
||||||
|
echo -e "Tag: ${BLUE}$new_tag${OFF} is valid"
|
||||||
|
else
|
||||||
|
# Release tag is not `vX.X.X` format
|
||||||
|
echo -e "Tag: ${BLUE}$new_tag${OFF} is ${RED}not valid${OFF} (must be in vX.X.X format)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Tag the new release
|
||||||
|
git tag -a "$new_tag" -m "$new_tag Release"
|
||||||
|
echo -e "${GREEN}Tagged: $new_tag${OFF}"
|
||||||
|
|
||||||
|
# Push the new tag to the remote
|
||||||
|
git push --tags
|
||||||
|
echo -e "${GREEN}Release tag pushed to remote${OFF}"
|
||||||
|
echo -e "${GREEN}Done!${OFF}"
|
46
src/asset-lookup.ts
Normal file
46
src/asset-lookup.ts
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
import { Octokit } from 'octokit'
|
||||||
|
import { components } from '@octokit/openapi-types/types'
|
||||||
|
|
||||||
|
export interface IGithubRelease {
|
||||||
|
readonly tag_name: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IReleaseTransformer<T extends IGithubRelease> {
|
||||||
|
map(release: components['schemas']['release']): T
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IReleaseLookup {
|
||||||
|
getRelease<T extends IGithubRelease>(
|
||||||
|
user: string,
|
||||||
|
repo: string,
|
||||||
|
version: string | undefined,
|
||||||
|
transformer: IReleaseTransformer<T>
|
||||||
|
): Promise<T>
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OctokitReleaseLookup implements IReleaseLookup {
|
||||||
|
octokit: Octokit
|
||||||
|
|
||||||
|
constructor(pat?: string) {
|
||||||
|
this.octokit = new Octokit({ auth: pat })
|
||||||
|
}
|
||||||
|
|
||||||
|
async getRelease<T extends IGithubRelease>(
|
||||||
|
owner: string,
|
||||||
|
repo: string,
|
||||||
|
version: string | undefined,
|
||||||
|
transformer: IReleaseTransformer<T>
|
||||||
|
): Promise<T> {
|
||||||
|
const latestRelease = version
|
||||||
|
? await this.octokit.rest.repos.getReleaseByTag({
|
||||||
|
owner: owner,
|
||||||
|
repo: repo,
|
||||||
|
tag: version
|
||||||
|
})
|
||||||
|
: await this.octokit.rest.repos.getLatestRelease({
|
||||||
|
owner: owner,
|
||||||
|
repo: repo
|
||||||
|
})
|
||||||
|
return transformer.map(latestRelease.data)
|
||||||
|
}
|
||||||
|
}
|
20
src/cache/download.ts
vendored
Normal file
20
src/cache/download.ts
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import * as tc from '@actions/tool-cache'
|
||||||
|
import { Hugo as HugoTool } from '../constants'
|
||||||
|
import * as io from '@actions/io'
|
||||||
|
|
||||||
|
async function downloadTool(
|
||||||
|
toolURL: string,
|
||||||
|
binDir: string,
|
||||||
|
tempDir: string
|
||||||
|
): Promise<void> {
|
||||||
|
const toolAssets: string = await tc.downloadTool(toolURL)
|
||||||
|
let toolBin = ''
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
const toolExtractedFolder: string = await tc.extractZip(toolAssets, tempDir)
|
||||||
|
toolBin = `${toolExtractedFolder}/${HugoTool.CmdName}.exe`
|
||||||
|
} else {
|
||||||
|
const toolExtractedFolder: string = await tc.extractTar(toolAssets, tempDir)
|
||||||
|
toolBin = `${toolExtractedFolder}/${HugoTool.CmdName}`
|
||||||
|
}
|
||||||
|
await io.mv(toolBin, binDir)
|
||||||
|
}
|
19
src/constants.ts
Normal file
19
src/constants.ts
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
export enum Hugo {
|
||||||
|
Name = 'Hugo',
|
||||||
|
Org = 'gohugoio',
|
||||||
|
Repo = 'hugo',
|
||||||
|
CmdName = 'hugo',
|
||||||
|
CmdOptVersion = 'version',
|
||||||
|
TestVersionLatest = '0.83.1',
|
||||||
|
TestVersionSpec = '0.82.1'
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum DartSass {
|
||||||
|
Org = 'sass',
|
||||||
|
Repo = 'dart-sass'
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum Action {
|
||||||
|
WorkDirName = 'actions_hugo',
|
||||||
|
TempDirName = '_temp'
|
||||||
|
}
|
90
src/dart-sass.ts
Normal file
90
src/dart-sass.ts
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
import { IGithubRelease, IReleaseLookup } from './asset-lookup'
|
||||||
|
import { DartSass } from './constants'
|
||||||
|
import { components } from '@octokit/openapi-types'
|
||||||
|
import * as core from '@actions/core'
|
||||||
|
import { Platform } from './os'
|
||||||
|
import { downloadTool } from '@actions/tool-cache'
|
||||||
|
|
||||||
|
export interface IDartSassInstallCommand {
|
||||||
|
version: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DartSassInstaller {
|
||||||
|
private readonly releaseLookup: IReleaseLookup
|
||||||
|
private readonly platform: Platform
|
||||||
|
|
||||||
|
constructor(releaseLookup: IReleaseLookup) {
|
||||||
|
this.platform = new Platform()
|
||||||
|
this.releaseLookup = releaseLookup
|
||||||
|
}
|
||||||
|
|
||||||
|
async install(cmd: IDartSassInstallCommand): Promise<void> {
|
||||||
|
const release = await this.releaseLookup.getRelease(
|
||||||
|
DartSass.Org,
|
||||||
|
DartSass.Repo,
|
||||||
|
cmd.version,
|
||||||
|
DartSassReleaseTransformer
|
||||||
|
)
|
||||||
|
|
||||||
|
core.debug(`Operating System: ${this.platform.os}`)
|
||||||
|
core.debug(`Processor Architecture: ${this.platform.arch}`)
|
||||||
|
|
||||||
|
const workDir = await this.platform.createWorkDir()
|
||||||
|
const binDir = await this.platform.createBinDir(workDir)
|
||||||
|
const tempDir = await this.platform.createTempDir(workDir)
|
||||||
|
|
||||||
|
const toolUrl = release.assetUrl(this.platform)
|
||||||
|
|
||||||
|
if (!toolUrl) {
|
||||||
|
throw new Error('No matching URL detected for given platform')
|
||||||
|
}
|
||||||
|
|
||||||
|
await downloadTool(toolUrl, binDir, tempDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DartSassReleaseTransformer = {
|
||||||
|
map(release: components['schemas']['release']): DartSassRelease {
|
||||||
|
return new DartSassRelease(
|
||||||
|
release.tag_name.replace('v', ''),
|
||||||
|
release.assets
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DartSassRelease implements IGithubRelease {
|
||||||
|
private static readonly keyReplacementRegex = new RegExp(
|
||||||
|
'dart-sass-*(\\d+.\\d+.\\d+)-'
|
||||||
|
)
|
||||||
|
private static readonly platformMapping: { [index: string]: string } = {
|
||||||
|
linux: 'linux',
|
||||||
|
win32: 'windows',
|
||||||
|
darwin: 'macos'
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly assets: Map<string, string>
|
||||||
|
|
||||||
|
readonly tag_name: string
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
tag_name: string,
|
||||||
|
assets: components['schemas']['release']['assets']
|
||||||
|
) {
|
||||||
|
this.tag_name = tag_name
|
||||||
|
this.assets = new Map<string, string>()
|
||||||
|
|
||||||
|
assets.forEach(asset => {
|
||||||
|
this.assets.set(
|
||||||
|
asset.name.replace(DartSassRelease.keyReplacementRegex, ''),
|
||||||
|
asset.url
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
assetUrl(platform: Platform): string | undefined {
|
||||||
|
const mappedOS = DartSassRelease.platformMapping[platform.os]
|
||||||
|
return this.assets.get(
|
||||||
|
`${mappedOS}-${platform.arch}${platform.archiveExtension()}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
94
src/hugo.ts
Normal file
94
src/hugo.ts
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
import * as core from '@actions/core'
|
||||||
|
import { Hugo } from './constants'
|
||||||
|
import { IGithubRelease, IReleaseLookup } from './asset-lookup'
|
||||||
|
import { Platform } from './os'
|
||||||
|
import { components } from '@octokit/openapi-types'
|
||||||
|
import { downloadTool } from '@actions/tool-cache'
|
||||||
|
|
||||||
|
export interface IHugoInstallCommand {
|
||||||
|
version: string
|
||||||
|
extended: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export class HugoInstaller {
|
||||||
|
private readonly releaseLookup: IReleaseLookup
|
||||||
|
private readonly platform: Platform
|
||||||
|
|
||||||
|
constructor(releaseLookup: IReleaseLookup) {
|
||||||
|
this.platform = new Platform()
|
||||||
|
this.releaseLookup = releaseLookup
|
||||||
|
}
|
||||||
|
|
||||||
|
async install(cmd: IHugoInstallCommand): Promise<void> {
|
||||||
|
const release = await this.releaseLookup.getRelease(
|
||||||
|
Hugo.Org,
|
||||||
|
Hugo.Repo,
|
||||||
|
cmd.version,
|
||||||
|
HugoReleaseTransformer
|
||||||
|
)
|
||||||
|
|
||||||
|
core.debug(`Hugo extended: ${cmd.extended}`)
|
||||||
|
core.debug(`Operating System: ${this.platform.os}`)
|
||||||
|
core.debug(`Processor Architecture: ${this.platform.arch}`)
|
||||||
|
|
||||||
|
const workDir = await this.platform.createWorkDir()
|
||||||
|
const binDir = await this.platform.createBinDir(workDir)
|
||||||
|
const tempDir = await this.platform.createTempDir(workDir)
|
||||||
|
|
||||||
|
const toolUrl = release.assetUrl(this.platform, cmd.extended)
|
||||||
|
|
||||||
|
if (!toolUrl) {
|
||||||
|
throw new Error('No matching URL detected for given platform')
|
||||||
|
}
|
||||||
|
|
||||||
|
await downloadTool(toolUrl, binDir, tempDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const HugoReleaseTransformer = {
|
||||||
|
map(release: components['schemas']['release']): HugoRelease {
|
||||||
|
return new HugoRelease(release.tag_name.replace('v', ''), release.assets)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class HugoRelease implements IGithubRelease {
|
||||||
|
private static readonly keyReplacementRegex = new RegExp(
|
||||||
|
'hugo_(extended_)*(\\d+.\\d+.\\d+)_'
|
||||||
|
)
|
||||||
|
|
||||||
|
readonly tag_name: string
|
||||||
|
|
||||||
|
private readonly defaultAssets: Map<string, string>
|
||||||
|
private readonly extendedAssets: Map<string, string>
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
tag_name: string,
|
||||||
|
assets: components['schemas']['release']['assets']
|
||||||
|
) {
|
||||||
|
this.tag_name = tag_name
|
||||||
|
this.defaultAssets = new Map<string, string>()
|
||||||
|
this.extendedAssets = new Map<string, string>()
|
||||||
|
|
||||||
|
assets.forEach(asset => {
|
||||||
|
if (asset.name.includes('extended')) {
|
||||||
|
this.extendedAssets.set(
|
||||||
|
asset.name.replace(HugoRelease.keyReplacementRegex, ''),
|
||||||
|
asset.url
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
this.defaultAssets.set(
|
||||||
|
asset.name.replace(HugoRelease.keyReplacementRegex, ''),
|
||||||
|
asset.url
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
assetUrl(platform: Platform, extended: boolean): string | undefined {
|
||||||
|
const src = extended ? this.extendedAssets : this.defaultAssets
|
||||||
|
const arch = platform.os === 'darwin' ? 'universal' : platform.arch
|
||||||
|
const key = `${platform.os}-${arch}${platform.archiveExtension()}`
|
||||||
|
|
||||||
|
return src.get(key)
|
||||||
|
}
|
||||||
|
}
|
9
src/index.ts
Normal file
9
src/index.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import * as core from '@actions/core'
|
||||||
|
import * as main from './main'
|
||||||
|
;(async (): Promise<void> => {
|
||||||
|
try {
|
||||||
|
await main.run()
|
||||||
|
} catch (e: any) {
|
||||||
|
core.setFailed(`Action failed with error ${e.message}`)
|
||||||
|
}
|
||||||
|
})()
|
23
src/main.ts
Normal file
23
src/main.ts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import * as core from '@actions/core'
|
||||||
|
import { HugoInstaller } from './hugo'
|
||||||
|
import { IReleaseLookup, OctokitReleaseLookup } from './asset-lookup'
|
||||||
|
import { DartSassInstaller } from './dart-sass'
|
||||||
|
|
||||||
|
export async function run(): Promise<void> {
|
||||||
|
const releaseLookup: IReleaseLookup = new OctokitReleaseLookup(
|
||||||
|
core.getInput('github-token')
|
||||||
|
)
|
||||||
|
const hugoInstaller = new HugoInstaller(releaseLookup)
|
||||||
|
|
||||||
|
await hugoInstaller.install({
|
||||||
|
version: core.getInput('hugo-version'),
|
||||||
|
extended: core.getBooleanInput('extended')
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!core.getBooleanInput('dart-sass')) return
|
||||||
|
|
||||||
|
const dartSassInstaller = new DartSassInstaller(releaseLookup)
|
||||||
|
await dartSassInstaller.install({
|
||||||
|
version: core.getInput('dart-sass-version')
|
||||||
|
})
|
||||||
|
}
|
65
src/os.ts
Normal file
65
src/os.ts
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
import * as process from 'node:process'
|
||||||
|
import path from 'path'
|
||||||
|
import { Action } from './constants'
|
||||||
|
import * as io from '@actions/io'
|
||||||
|
import * as core from '@actions/core'
|
||||||
|
|
||||||
|
interface Env {
|
||||||
|
[key: string]: string | undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Platform {
|
||||||
|
os: string
|
||||||
|
arch: string
|
||||||
|
env: Env
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
os: string = process.platform,
|
||||||
|
arch: string = process.arch,
|
||||||
|
env: Env = process.env
|
||||||
|
) {
|
||||||
|
this.os = os
|
||||||
|
this.arch = arch
|
||||||
|
this.env = env
|
||||||
|
}
|
||||||
|
|
||||||
|
archiveExtension(): string {
|
||||||
|
if (this.os === 'win32') {
|
||||||
|
return '.zip'
|
||||||
|
}
|
||||||
|
return '.tar.gz'
|
||||||
|
}
|
||||||
|
|
||||||
|
async createWorkDir(): Promise<string> {
|
||||||
|
const workDir = path.join(this.getHomeDir(), Action.WorkDirName)
|
||||||
|
await io.mkdirP(workDir)
|
||||||
|
core.debug(`workDir: ${workDir}`)
|
||||||
|
return workDir
|
||||||
|
}
|
||||||
|
|
||||||
|
async createTempDir(workDir: string): Promise<string> {
|
||||||
|
const tempDir = path.join(workDir, Action.TempDirName)
|
||||||
|
await io.mkdirP(tempDir)
|
||||||
|
core.debug(`tempDir: ${tempDir}`)
|
||||||
|
return tempDir
|
||||||
|
}
|
||||||
|
|
||||||
|
async createBinDir(workDir: string): Promise<string> {
|
||||||
|
const binDir = path.join(workDir, 'bin')
|
||||||
|
await io.mkdirP(binDir)
|
||||||
|
core.addPath(binDir)
|
||||||
|
core.debug(`binDir: ${binDir}`)
|
||||||
|
return binDir
|
||||||
|
}
|
||||||
|
|
||||||
|
getHomeDir(): string {
|
||||||
|
const homedir =
|
||||||
|
this.os === 'win32'
|
||||||
|
? this.env['USERPROFILE'] || 'C:\\'
|
||||||
|
: this.env.HOME || '/root'
|
||||||
|
|
||||||
|
core.debug(`homeDir: ${homedir}`)
|
||||||
|
|
||||||
|
return homedir
|
||||||
|
}
|
||||||
|
}
|
18
tsconfig.json
Normal file
18
tsconfig.json
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/tsconfig",
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"rootDir": "./src",
|
||||||
|
"moduleResolution": "Node",
|
||||||
|
"baseUrl": "./",
|
||||||
|
"sourceMap": true,
|
||||||
|
"outDir": "./dist",
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"newLine": "lf"
|
||||||
|
},
|
||||||
|
"exclude": ["./dist", "./node_modules", "./__tests__", "./coverage"]
|
||||||
|
}
|
Loading…
Reference in a new issue