Compare commits
58 Commits
79dddea92c
...
test-fix-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
8ca9c67979
|
|||
| 48f7596789 | |||
|
ae87ee6e35
|
|||
| 97ecdcbc66 | |||
|
6ff8731e50
|
|||
|
b7136de5e2
|
|||
| de6b54abe9 | |||
| 60d3342a1c | |||
|
e5773d3235
|
|||
|
0d97efec68
|
|||
| 9e9740b8b1 | |||
| 65486738c0 | |||
|
a2b753417a
|
|||
| 25e4ba9f71 | |||
| 76eaa21fb3 | |||
| e39a879879 | |||
| 291ffc41bc | |||
|
60c6779c64
|
|||
|
fb80f152df
|
|||
| 1da1212c8f | |||
| a892e71b21 | |||
| 74d74b73ad | |||
| 9510650dd1 | |||
| 96da456932 | |||
| d95bfe4b61 | |||
| 19259737af | |||
| ac36a7ab1f | |||
| 0780c1e8ab | |||
| 9d9dc1e822 | |||
| 82e4d66d61 | |||
| 01deab89cd | |||
| 28b0ab9f02 | |||
| 0c37ffc7c7 | |||
|
|
9c79fe03b0 | ||
| 6f0f75d7ad | |||
| 318515b9c4 | |||
| 1aa7be2b73 | |||
| 567eb22cd8 | |||
|
|
898a9aae6d | ||
| 3b0920a44a | |||
|
|
dec00551b1 | ||
|
|
8309164978 | ||
|
|
7f0b32347e | ||
| 508eec34d7 | |||
| bee951fc34 | |||
|
|
3e3d24f787 | ||
| e342ceca6f | |||
|
|
a039bc1e28 | ||
|
|
0a952729bc | ||
|
|
f106f83c72 | ||
|
|
cd7745d646 | ||
|
4951253d52
|
|||
| e7678a5f66 | |||
| 6f5729c12a | |||
|
|
0858cc69be | ||
| df14b016b7 | |||
|
|
f1581c908b | ||
|
|
d04a264bc4 |
37
.devcontainer/devcontainer.json
Normal file
37
.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"name": "JS Dev Container",
|
||||||
|
"image": "mcr.microsoft.com/devcontainers/javascript-node:4-20",
|
||||||
|
"features": {
|
||||||
|
"ghcr.io/devcontainers/features/git:1": {},
|
||||||
|
"ghcr.io/devcontainers/features/git-lfs:1": {},
|
||||||
|
"ghcr.io/devcontainers-extra/features/pre-commit:2": {},
|
||||||
|
"ghcr.io/devcontainers-extra/features/npm-packages:1": {
|
||||||
|
"packages": "@google/gemini-cli"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"customizations": {
|
||||||
|
"vscode": {
|
||||||
|
"extensions": [
|
||||||
|
"ms-python.python",
|
||||||
|
"esbenp.prettier-vscode",
|
||||||
|
"vitest.explorer",
|
||||||
|
"google.gemini-cli-vscode-ide-companion",
|
||||||
|
"google.geminicodeassist"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"postStartCommand": "bash .devcontainer/setup.sh",
|
||||||
|
"remoteUser": "node",
|
||||||
|
"updateRemoteUserUID": true,
|
||||||
|
"mounts": [
|
||||||
|
"source=pipx-venvs,target=/home/node/.local/share/pipx,type=volume",
|
||||||
|
"source=pipx-bin,target=/home/node/.local/bin,type=volume",
|
||||||
|
"source=pre-commit-cache,target=/home/node/.cache/pre-commit,type=volume",
|
||||||
|
"source=${localEnv:USERPROFILE}/.gemini,target=/home/node/.gemini,type=bind",
|
||||||
|
"source=gvscode-extensions,target=/home/node/.cache/google-vscode-extension,type=volume"
|
||||||
|
]
|
||||||
|
}
|
||||||
11
.devcontainer/setup.sh
Normal file
11
.devcontainer/setup.sh
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# 1. Fix Git Permissions (Critical in Docker)
|
||||||
|
git config --global --add safe.directory $(pwd)
|
||||||
|
|
||||||
|
# In your setup.sh or postCreateCommand
|
||||||
|
sudo chown -R node:node /home/node/.cache/
|
||||||
|
|
||||||
|
# 2. Re-connect Git Hooks
|
||||||
|
pre-commit install
|
||||||
|
pre-commit install-hooks
|
||||||
@@ -9,8 +9,8 @@ jobs:
|
|||||||
name: pre-commit Linting
|
name: pre-commit Linting
|
||||||
runs-on: pi
|
runs-on: pi
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v6
|
||||||
- run: pip install pre-commit
|
- run: pip install pre-commit
|
||||||
shell: bash
|
shell: bash
|
||||||
- name: Pre Commit
|
- name: Pre Commit
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
name: Auto Maintenance Cycle
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
paths:
|
|
||||||
- .gitea/workflows/maintenance.yaml
|
|
||||||
schedule:
|
|
||||||
- cron: 0 1 * * 0
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
auto-update:
|
|
||||||
name: pre-commit Autoupdate
|
|
||||||
runs-on: pi64
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/setup-python@v4
|
|
||||||
- run: pip install pre-commit
|
|
||||||
shell: bash
|
|
||||||
- run: pre-commit autoupdate
|
|
||||||
shell: bash
|
|
||||||
- name: Test pre-commit
|
|
||||||
run: SKIP=no-commit-to-branch pre-commit run -a
|
|
||||||
- name: Commit
|
|
||||||
id: auto-commit-action
|
|
||||||
uses: stefanzweifel/git-auto-commit-action@v5
|
|
||||||
with:
|
|
||||||
commit_message: 'chore: update pre-commit hooks'
|
|
||||||
branch: update/pre-commit-hooks
|
|
||||||
push_options: --force
|
|
||||||
create_branch: true
|
|
||||||
- name: Generate Date
|
|
||||||
run: echo "CURRENT_DATE=$(date +'%Y-%m-%d')" >> $GITHUB_ENV
|
|
||||||
- run: echo "${{steps.auto-commit-action.outputs.changes_detected}}"
|
|
||||||
- name: Create an PR action
|
|
||||||
if: steps.auto-commit-action.outputs.changes_detected == 'true'
|
|
||||||
uses: https://git.horstenkamp.eu/Philipp/gitea-act-create-pr@main
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.REPO_TOKEN }}
|
|
||||||
branch: update/pre-commit-hooks
|
|
||||||
title: Updates to the pre-commit action created at ${{ env.CURRENT_DATE }}
|
|
||||||
body: Update to the pre-commit action.
|
|
||||||
assignees: Philipp
|
|
||||||
reviewers: Philipp
|
|
||||||
19
.gitea/workflows/test.yaml
Normal file
19
.gitea/workflows/test.yaml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
name: Test
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
name: Run Tests
|
||||||
|
runs-on: pi
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '22'
|
||||||
|
- run: npm install
|
||||||
|
shell: bash
|
||||||
|
- run: npm test
|
||||||
|
shell: bash
|
||||||
1078
.gitignore
vendored
Normal file
1078
.gitignore
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,17 +1,15 @@
|
|||||||
|
exclude: ^(dist|node_modules)/
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: v4.5.0
|
rev: v6.0.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: end-of-file-fixer
|
|
||||||
exclude: (.txt$|.ipynb$|README.md$|readme.mde$)
|
|
||||||
- id: trailing-whitespace
|
|
||||||
exclude: (.txt$|README.md$)
|
|
||||||
- id: check-yaml
|
- id: check-yaml
|
||||||
- id: check-json
|
- id: check-json
|
||||||
- id: check-toml
|
- id: check-toml
|
||||||
- id: check-xml
|
- id: check-xml
|
||||||
- id: check-added-large-files
|
- id: check-added-large-files
|
||||||
args: [--enforce-all]
|
args: [--enforce-all]
|
||||||
|
exclude: ^dist/index\.js$
|
||||||
- id: name-tests-test
|
- id: name-tests-test
|
||||||
- id: detect-private-key
|
- id: detect-private-key
|
||||||
- id: check-case-conflict
|
- id: check-case-conflict
|
||||||
@@ -21,10 +19,9 @@ repos:
|
|||||||
args: [--autofix, --no-sort-keys, --no-ensure-ascii]
|
args: [--autofix, --no-sort-keys, --no-ensure-ascii]
|
||||||
- id: check-merge-conflict
|
- id: check-merge-conflict
|
||||||
- id: no-commit-to-branch
|
- id: no-commit-to-branch
|
||||||
args: [--branch, main]
|
|
||||||
|
|
||||||
- repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks
|
- repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks
|
||||||
rev: v2.11.0
|
rev: v2.15.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: pretty-format-ini
|
- id: pretty-format-ini
|
||||||
args: [--autofix]
|
args: [--autofix]
|
||||||
@@ -33,18 +30,25 @@ repos:
|
|||||||
- id: pretty-format-yaml
|
- id: pretty-format-yaml
|
||||||
args: [--autofix]
|
args: [--autofix]
|
||||||
|
|
||||||
- repo: https://github.com/frnmst/md-toc
|
|
||||||
rev: 8.2.0
|
|
||||||
hooks:
|
|
||||||
- id: md-toc
|
|
||||||
|
|
||||||
- repo: https://github.com/Lucas-C/pre-commit-hooks-java
|
|
||||||
rev: 1.3.10
|
|
||||||
hooks:
|
|
||||||
- id: validate-html
|
|
||||||
|
|
||||||
- repo: https://github.com/pre-commit/mirrors-prettier
|
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||||
rev: v3.1.0
|
rev: v4.0.0-alpha.8
|
||||||
hooks:
|
hooks:
|
||||||
- id: prettier
|
- id: prettier
|
||||||
types_or: [css, javascript]
|
types_or: [css, javascript]
|
||||||
|
|
||||||
|
- repo: https://github.com/python-jsonschema/check-jsonschema
|
||||||
|
rev: 0.36.0
|
||||||
|
hooks:
|
||||||
|
- id: check-renovate
|
||||||
|
- id: check-github-actions
|
||||||
|
- id: check-github-workflows
|
||||||
|
|
||||||
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
|
rev: v6.0.0
|
||||||
|
hooks:
|
||||||
|
- id: end-of-file-fixer
|
||||||
|
exclude: (.txt$|.ipynb$|README.md$|readme.mde$)
|
||||||
|
- id: trailing-whitespace
|
||||||
|
exclude: (.txt$|README.md$)
|
||||||
|
- id: mixed-line-ending
|
||||||
|
args: [--fix=lf]
|
||||||
|
|||||||
13
.vscode/tasks.json
vendored
Normal file
13
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"type": "npm",
|
||||||
|
"script": "test",
|
||||||
|
"group": "test",
|
||||||
|
"problemMatcher": [],
|
||||||
|
"label": "npm: test",
|
||||||
|
"detail": "jest"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
33
GEMINI.md
Normal file
33
GEMINI.md
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# Gemini Actions
|
||||||
|
|
||||||
|
This repository is maintained by Gemini.
|
||||||
|
|
||||||
|
## Development Guidelines
|
||||||
|
|
||||||
|
* **Test-Driven Development (TDD):** Wherever possible, Test-Driven Development principles should be followed. Write tests before writing the code they are intended to validate.
|
||||||
|
* **Pre-commit Hooks:** Ensure that `pre-commit` hooks are installed and active before making any commits. This can be done by running `pre-commit install` in your local repository.
|
||||||
|
|
||||||
|
## Repository Comparison
|
||||||
|
|
||||||
|
* On request, this repository should be compared against the rules and guidelines specified in the `README.md` of the reference repository: `https://git.horstenkamp.eu/Philipp/template-git`.
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
This project uses [Vitest](https://vitest.dev/) for testing. The tests are located in the `__tests__` directory.
|
||||||
|
|
||||||
|
To run the tests locally, use the following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm test
|
||||||
|
```
|
||||||
|
|
||||||
|
### Testing Pipeline
|
||||||
|
|
||||||
|
The tests are automatically run on every push and workflow dispatch using a Gitea workflow. The workflow is defined in `.gitea/workflows/test.yaml`. All testing for this repository is done via Gitea workflows, not GitHub workflows.
|
||||||
|
|
||||||
|
The Gitea workflow does the following:
|
||||||
|
|
||||||
|
1. Checks out the code.
|
||||||
|
2. Sets up Node.js.
|
||||||
|
3. Installs the dependencies using `npm install`.
|
||||||
|
4. Runs the tests using `npm test`.
|
||||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2023 Screeps
|
Copyright (c) 2023 Philipp Horstenkamp
|
||||||
|
|
||||||
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:
|
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:
|
||||||
|
|
||||||
|
|||||||
109
README.md
109
README.md
@@ -1,3 +1,110 @@
|
|||||||
# screeps-deploy-action
|
# screeps-deploy-action
|
||||||
|
|
||||||
This action deploys screeps code via github / gitea actions.
|
## Introduction
|
||||||
|
|
||||||
|
This GitHub Action facilitates the uploading of code to a Screeps server.
|
||||||
|
It's designed to automate the process of deploying JavaScript code to your Screeps account, ensuring that your game logic is consistently and efficiently updated.
|
||||||
|
Prerequisites
|
||||||
|
|
||||||
|
A Screeps account with an access token.
|
||||||
|
A GitHub or Gitea repository with your Screeps code.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
To use this action, you need to set it up in your workflow .yml file located in the .github/workflows directory of your repository.
|
||||||
|
|
||||||
|
## Inputs
|
||||||
|
|
||||||
|
- `protocol`: The protocol to use (default: https).
|
||||||
|
- `hostname`: The hostname of the Screeps server (default: screeps.com).
|
||||||
|
- `port`: The port to use (default: 443).
|
||||||
|
- `path`: The path for the API (default: /).
|
||||||
|
- `token`: Authentication token for Screeps.
|
||||||
|
- `username`: Username for Screeps account (used if no token is provided).
|
||||||
|
- `password`: Password for Screeps account (used if no token is provided).
|
||||||
|
- `prefix`: Directory prefix for file paths.
|
||||||
|
- `pattern`: Glob pattern to match files (default: *.js).
|
||||||
|
- `branch`: Branch in Screeps to which the code will be uploaded (default: default).
|
||||||
|
- `git-replace`: Overwrite "{{gitRef}}", "{{gitHash}}" and "{{deployTime}}" values in files matching the pattern.
|
||||||
|
|
||||||
|
Example Workflow
|
||||||
|
|
||||||
|
Create a `.yml` file (e.g., `screeps-deploy.yml`) in your repository's `.github/workflows` directory or `.gitea/workflows` directory:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
name: Deploy to Screeps
|
||||||
|
|
||||||
|
on: [push]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Upload to Screeps
|
||||||
|
uses: Screeps/screeps-deploy-action@main
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.SCREEPS_TOKEN }}
|
||||||
|
pattern: '**/*.js'
|
||||||
|
branch: 'default'
|
||||||
|
git-replace: /some_options.js
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example:
|
||||||
|
|
||||||
|
- The action runs on every push to the repository.
|
||||||
|
- It checks out your code.
|
||||||
|
- Then, it uses the "Upload to Screeps" action to deploy the code to your Screeps account.
|
||||||
|
- You need to set SCREEPS_TOKEN in your repository secrets.
|
||||||
|
|
||||||
|
## Advanced Usage
|
||||||
|
|
||||||
|
Please note that you can easily filter your deployment branches in the push action.
|
||||||
|
Multiple deploy steps or jobs are recomended for mulitple deployments.
|
||||||
|
|
||||||
|
## Development & Build
|
||||||
|
|
||||||
|
This project includes a build step to prepare the action for use.
|
||||||
|
|
||||||
|
### Why a Build Step?
|
||||||
|
|
||||||
|
We use a build step to compile the JavaScript code and its dependencies into a single, self-contained file (`dist/index.js`). This approach is chosen for the following reasons:
|
||||||
|
1. **Zero-configuration at Runtime:** By bundling everything, the action runner doesn't need to run `npm install`, which speeds up the action execution.
|
||||||
|
2. **Reliability:** It guarantees that the exact versions of dependencies tested during development are used in the action, avoiding potential issues with version updates or missing packages.
|
||||||
|
3. **Standard Practice:** This is a recommended practice for JavaScript-based GitHub/Gitea Actions.
|
||||||
|
|
||||||
|
### The Tool: @vercel/ncc
|
||||||
|
|
||||||
|
We chose [`@vercel/ncc`](https://github.com/vercel/ncc) as the compiler because:
|
||||||
|
- It is designed specifically for compiling Node.js modules into a single file.
|
||||||
|
- It handles native dependencies and dynamic requires intelligently.
|
||||||
|
- It is zero-config, making the setup simple and maintainable.
|
||||||
|
|
||||||
|
### How to Build
|
||||||
|
|
||||||
|
If you modify `index.js` or change dependencies, you must rebuild the project before committing:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
This will update the `dist/index.js` file, which should be committed to the repository.
|
||||||
|
|
||||||
|
### Cross-Platform Compatibility
|
||||||
|
|
||||||
|
To ensure this action runs on all platforms (including ARM devices like Raspberry Pi), we explicitly exclude optional native dependencies (`utf-8-validate` and `bufferutil`) from the build. This forces the underlying `ws` library to use its pure JavaScript fallback implementation.
|
||||||
|
|
||||||
|
### Build Protocol
|
||||||
|
|
||||||
|
| Metric | Status / Value |
|
||||||
|
| :--- | :--- |
|
||||||
|
| **Last Build** | Saturday, December 27, 2025 |
|
||||||
|
| **Build Tool** | `@vercel/ncc` |
|
||||||
|
| **Target Runtime** | Node.js 20 |
|
||||||
|
| **Artifact** | `dist/index.js` (Self-contained) |
|
||||||
|
| **Native Binaries** | None (Excluded for cross-platform support) |
|
||||||
|
| **Compatibility** | Linux (x64/ARM), Windows, macOS |
|
||||||
|
| **Build Status** | ✅ Success |
|
||||||
|
|||||||
117
__tests__/index.test.js
Normal file
117
__tests__/index.test.js
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
const { validateAuthentication, replacePlaceholders } = require("../index");
|
||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
const os = require("os");
|
||||||
|
const { glob } = require("glob");
|
||||||
|
|
||||||
|
describe("validateAuthentication", () => {
|
||||||
|
it("should return null when only token is provided", () => {
|
||||||
|
expect(validateAuthentication("token", null, null)).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return an error message when token and username are provided", () => {
|
||||||
|
expect(validateAuthentication("token", "user", null)).toBe(
|
||||||
|
"Token is defined along with username and/or password.",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return an error message when token and password are provided", () => {
|
||||||
|
expect(validateAuthentication("token", null, "pass")).toBe(
|
||||||
|
"Token is defined along with username and/or password.",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return an error message when token, username, and password are provided", () => {
|
||||||
|
expect(validateAuthentication("token", "user", "pass")).toBe(
|
||||||
|
"Token is defined along with username and/or password.",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return an error message when no credentials are provided", () => {
|
||||||
|
expect(validateAuthentication(null, null, null)).toBe(
|
||||||
|
"Neither token nor password and username are defined.",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return an error message when only username is provided", () => {
|
||||||
|
expect(validateAuthentication(null, "user", null)).toBe(
|
||||||
|
"Username is defined but no password is provided.",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return an error message when only password is provided", () => {
|
||||||
|
expect(validateAuthentication(null, null, "pass")).toBe(
|
||||||
|
"Password is defined but no username is provided.",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return null when username and password are provided", () => {
|
||||||
|
expect(validateAuthentication(null, "user", "pass")).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("replacePlaceholders", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
process.env.GITHUB_SHA = "test-sha";
|
||||||
|
process.env.GITHUB_REF = "test-ref";
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should replace all placeholders", () => {
|
||||||
|
const content =
|
||||||
|
"hash: {{gitHash}}, ref: {{gitRef}}, time: {{deployTime}}, host: {{hostname}}";
|
||||||
|
const replacedContent = replacePlaceholders(content, "test-host");
|
||||||
|
expect(replacedContent).toMatch(/hash: test-sha/);
|
||||||
|
expect(replacedContent).toMatch(/ref: test-ref/);
|
||||||
|
expect(replacedContent).toMatch(/time: .*/);
|
||||||
|
expect(replacedContent).toMatch(/host: test-host/);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("glob functionality", () => {
|
||||||
|
let tempDir;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
tempDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), "glob-test-"));
|
||||||
|
await fs.promises.mkdir(path.join(tempDir, "lib"), { recursive: true });
|
||||||
|
await fs.promises.mkdir(path.join(tempDir, "deep", "folder"), {
|
||||||
|
recursive: true,
|
||||||
|
});
|
||||||
|
await fs.promises.writeFile(path.join(tempDir, "main.js"), "content");
|
||||||
|
await fs.promises.writeFile(path.join(tempDir, "utils.js"), "content");
|
||||||
|
await fs.promises.writeFile(
|
||||||
|
path.join(tempDir, "lib", "helper.js"),
|
||||||
|
"content",
|
||||||
|
);
|
||||||
|
await fs.promises.writeFile(
|
||||||
|
path.join(tempDir, "lib", "data.json"),
|
||||||
|
"content",
|
||||||
|
);
|
||||||
|
await fs.promises.writeFile(
|
||||||
|
path.join(tempDir, "deep", "folder", "main.js"),
|
||||||
|
"content",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
if (tempDir) {
|
||||||
|
await fs.promises.rm(tempDir, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should find all javascript files in the directory", async () => {
|
||||||
|
// Ensure pattern uses forward slashes for glob
|
||||||
|
const pattern = path.join(tempDir, "**", "*.js").split(path.sep).join("/");
|
||||||
|
const files = await glob(pattern);
|
||||||
|
|
||||||
|
// Normalize file paths to system separator (backslashes on Windows)
|
||||||
|
const normalizedFiles = files.map((f) => path.normalize(f));
|
||||||
|
|
||||||
|
const expectedFiles = [
|
||||||
|
path.join(tempDir, "deep", "folder", "main.js"),
|
||||||
|
path.join(tempDir, "lib", "helper.js"),
|
||||||
|
path.join(tempDir, "main.js"),
|
||||||
|
path.join(tempDir, "utils.js"),
|
||||||
|
].sort();
|
||||||
|
expect(normalizedFiles.sort()).toEqual(expectedFiles);
|
||||||
|
});
|
||||||
|
});
|
||||||
45
action.yaml
Normal file
45
action.yaml
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
name: Upload to Screeps
|
||||||
|
description: This action uploads code to the Screeps server.
|
||||||
|
inputs:
|
||||||
|
protocol:
|
||||||
|
description: 'The protocol to use (default: https).'
|
||||||
|
required: false
|
||||||
|
default: https
|
||||||
|
hostname:
|
||||||
|
description: 'The hostname of the Screeps server (default: screeps.com).'
|
||||||
|
required: false
|
||||||
|
default: screeps.com
|
||||||
|
port:
|
||||||
|
description: 'The port to use (default: 443).'
|
||||||
|
required: false
|
||||||
|
default: '443'
|
||||||
|
path:
|
||||||
|
description: The path for the API.
|
||||||
|
required: false
|
||||||
|
default: /
|
||||||
|
token:
|
||||||
|
description: Authentication token for Screeps.
|
||||||
|
required: true
|
||||||
|
username:
|
||||||
|
description: Username for Screeps account. Used if no token is provided.
|
||||||
|
required: false
|
||||||
|
password:
|
||||||
|
description: Password for Screeps account. Used if no token is provided.
|
||||||
|
required: false
|
||||||
|
prefix:
|
||||||
|
description: Directory prefix for file paths.
|
||||||
|
required: false
|
||||||
|
pattern:
|
||||||
|
description: 'Glob pattern to match files (default: *.js).'
|
||||||
|
required: false
|
||||||
|
default: '*.js'
|
||||||
|
branch:
|
||||||
|
description: 'Branch in Screeps to which the code will be uploaded (default: default).'
|
||||||
|
required: false
|
||||||
|
default: default
|
||||||
|
git-replace:
|
||||||
|
description: Allows for the overwrite of the "{{gitRef}}", "{{gitHash}}" and "{{deployTime}}" values in the file matching the file pattern. The file pattern will be combined with the prefix.
|
||||||
|
required: false
|
||||||
|
runs:
|
||||||
|
using: node20
|
||||||
|
main: dist/index.js
|
||||||
56808
dist/index.js
vendored
Normal file
56808
dist/index.js
vendored
Normal file
File diff suppressed because one or more lines are too long
182
index.js
Normal file
182
index.js
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
const { ScreepsAPI } = require("screeps-api");
|
||||||
|
const core = require("@actions/core");
|
||||||
|
const fs = require("fs");
|
||||||
|
const { glob } = require("glob");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces specific placeholder strings within the provided content with corresponding dynamic values.
|
||||||
|
*
|
||||||
|
* This function specifically targets three placeholders:
|
||||||
|
* - {{gitHash}} is replaced with the current Git commit hash, obtained from the GITHUB_SHA environment variable.
|
||||||
|
* - {{gitRef}} is replaced with the Git reference (branch or tag) that triggered the workflow, obtained from the GITHUB_REF environment variable.
|
||||||
|
* - {{deployTime}} is replaced with the current ISO timestamp.
|
||||||
|
*
|
||||||
|
* Note: This function is designed for use within a GitHub Actions workflow where GITHUB_SHA and GITHUB_REF environment variables are automatically set.
|
||||||
|
*
|
||||||
|
* @param {string} content - The string content in which placeholders are to be replaced.
|
||||||
|
* @returns {string} The content with placeholders replaced by their respective dynamic values.
|
||||||
|
*/
|
||||||
|
function replacePlaceholders(content, hostname) {
|
||||||
|
const deployTime = new Date().toISOString();
|
||||||
|
return content
|
||||||
|
.replace(/{{gitHash}}/g, process.env.GITHUB_SHA)
|
||||||
|
.replace(/{{gitRef}}/g, process.env.GITHUB_REF)
|
||||||
|
.replace(/{{deployTime}}/g, deployTime)
|
||||||
|
.replace(/{{hostname}}/g, hostname);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads all files matching a specified pattern, replaces certain placeholders in their content, and writes the updated content back to the files.
|
||||||
|
*
|
||||||
|
* This function searches for files in the filesystem using the provided glob pattern, optionally prefixed. It reads each file,
|
||||||
|
* uses the `replacePlaceholders` function to replace specific placeholders in the file's content, and then writes the modified content
|
||||||
|
* back to the original file. This is useful for dynamically updating file contents in a batch process, such as during a build or deployment.
|
||||||
|
*
|
||||||
|
* @param {string} pattern - The glob pattern used to find files. Example: '*.js' for all JavaScript files.
|
||||||
|
* @param {string} [prefix] - An optional directory prefix to prepend to the glob pattern. This allows searching within a specific directory.
|
||||||
|
* @returns {Promise<string[]>} A promise that resolves with an array of file paths that were processed, or rejects with an error if the process fails.
|
||||||
|
*/
|
||||||
|
async function readReplaceAndWriteFiles(pattern, prefix, hostname) {
|
||||||
|
const globPattern = prefix ? path.join(prefix, pattern) : pattern;
|
||||||
|
const files = await glob(globPattern);
|
||||||
|
|
||||||
|
let processPromises = files.map((file) => {
|
||||||
|
return fs.promises.readFile(file, "utf8").then((content) => {
|
||||||
|
content = replacePlaceholders(content, hostname);
|
||||||
|
return fs.promises.writeFile(file, content);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.all(processPromises);
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads files matching a glob pattern into a dictionary.
|
||||||
|
* @param {string} pattern - Glob pattern to match files.
|
||||||
|
* @param {string} prefix - Directory prefix for file paths.
|
||||||
|
* @returns {Promise<Object>} - Promise resolving to a dictionary of file contents keyed by filenames.
|
||||||
|
*/
|
||||||
|
async function readFilesIntoDict(pattern, prefix) {
|
||||||
|
// Prepend the prefix to the glob pattern
|
||||||
|
const globPattern = prefix ? path.join(prefix, pattern) : pattern;
|
||||||
|
const files = await glob(globPattern);
|
||||||
|
|
||||||
|
let fileDict = {};
|
||||||
|
let readPromises = files.map((file) => {
|
||||||
|
return fs.promises.readFile(file, "utf8").then((content) => {
|
||||||
|
// Remove the prefix from the filename and drop the file suffix
|
||||||
|
let key = file;
|
||||||
|
if (prefix && file.startsWith(prefix)) {
|
||||||
|
key = key.slice(prefix.length);
|
||||||
|
}
|
||||||
|
key = path.basename(key, path.extname(key)); // Drop the file suffix
|
||||||
|
|
||||||
|
fileDict[key] = content;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.all(readPromises);
|
||||||
|
return fileDict;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the provided authentication credentials.
|
||||||
|
* @param {string} token - The authentication token.
|
||||||
|
* @param {string} username - The username.
|
||||||
|
* @param {string} password - The password.
|
||||||
|
* @returns {string|null} - Returns an error message if validation fails, otherwise null.
|
||||||
|
*/
|
||||||
|
function validateAuthentication(token, username, password) {
|
||||||
|
if (token) {
|
||||||
|
if (username || password) {
|
||||||
|
return "Token is defined along with username and/or password.";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!username && !password) {
|
||||||
|
return "Neither token nor password and username are defined.";
|
||||||
|
}
|
||||||
|
if (username && !password) {
|
||||||
|
return "Username is defined but no password is provided.";
|
||||||
|
}
|
||||||
|
if (!username && password) {
|
||||||
|
return "Password is defined but no username is provided.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null; // No errors found
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Posts code to Screeps server.
|
||||||
|
*/
|
||||||
|
async function postCode() {
|
||||||
|
const protocol = core.getInput("protocol") || "https";
|
||||||
|
const hostname = core.getInput("hostname") || "screeps.com";
|
||||||
|
const port = core.getInput("port") || "443";
|
||||||
|
const path = core.getInput("path") || "/";
|
||||||
|
|
||||||
|
const token = core.getInput("token") || undefined;
|
||||||
|
const username = core.getInput("username") || undefined;
|
||||||
|
const password = core.getInput("password") || undefined;
|
||||||
|
const prefix = core.getInput("source-prefix");
|
||||||
|
const pattern = core.getInput("pattern") || "*.js";
|
||||||
|
const branch = core.getInput("branch") || "default";
|
||||||
|
|
||||||
|
const gitReplace = core.getInput("git-replace") || null;
|
||||||
|
|
||||||
|
if (gitReplace) {
|
||||||
|
await readReplaceAndWriteFiles(gitReplace, prefix, hostname);
|
||||||
|
}
|
||||||
|
|
||||||
|
const files_to_push = await readFilesIntoDict(pattern, prefix);
|
||||||
|
|
||||||
|
core.info(`Trying to upload the following files to ${branch}:`);
|
||||||
|
Object.keys(files_to_push).forEach((key) => {
|
||||||
|
core.info(`Key: ${key}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
const login_arguments = {
|
||||||
|
token: token,
|
||||||
|
username: username,
|
||||||
|
password: password,
|
||||||
|
protocol: protocol,
|
||||||
|
hostname: hostname,
|
||||||
|
port: port,
|
||||||
|
path: path,
|
||||||
|
};
|
||||||
|
|
||||||
|
core.info("login_arguments:");
|
||||||
|
core.info(JSON.stringify(login_arguments, null, 2));
|
||||||
|
|
||||||
|
const errorMessage = validateAuthentication(token, username, password);
|
||||||
|
if (errorMessage) {
|
||||||
|
core.error(errorMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let api = new ScreepsAPI(login_arguments);
|
||||||
|
if (token) {
|
||||||
|
const response = await api.code.set(branch, files_to_push);
|
||||||
|
core.info(JSON.stringify(response, null, 2));
|
||||||
|
console.log(`Code set successfully to ${branch}`);
|
||||||
|
} else {
|
||||||
|
core.info(`Logging in as user ${username}`);
|
||||||
|
await Promise.resolve()
|
||||||
|
.then(() => api.auth(username, password, login_arguments))
|
||||||
|
.then(() => {
|
||||||
|
api.code.set(branch, files_to_push);
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
console.log(`Code set successfully to ${branch}`);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error("Error:", err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
postCode();
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
validateAuthentication,
|
||||||
|
replacePlaceholders,
|
||||||
|
};
|
||||||
2754
package-lock.json
generated
Normal file
2754
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
21
package.json
Normal file
21
package.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"name": "screeps-deploy-action",
|
||||||
|
"version": "0.1.1",
|
||||||
|
"description": "Deploys screeps code to the official game or an pirvate server.",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "node index.js",
|
||||||
|
"test": "vitest run --globals --coverage",
|
||||||
|
"build": "ncc build index.js -o dist --external utf-8-validate --external bufferutil"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@actions/core": "^1.11.1",
|
||||||
|
"glob": "^11.0.1",
|
||||||
|
"screeps-api": "^1.7.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vercel/ncc": "^0.38.4",
|
||||||
|
"@vitest/coverage-v8": "^4.0.16",
|
||||||
|
"vitest": "^4.0.16"
|
||||||
|
}
|
||||||
|
}
|
||||||
24
renovate.json
Normal file
24
renovate.json
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
|
"extends": [
|
||||||
|
"local>server/Renovate-Template:renovate.json",
|
||||||
|
"config:recommended",
|
||||||
|
":automergeLinters"
|
||||||
|
],
|
||||||
|
"packageRules": [
|
||||||
|
{
|
||||||
|
"matchManagers": [
|
||||||
|
"npm"
|
||||||
|
],
|
||||||
|
"postUpgradeTasks": {
|
||||||
|
"commands": [
|
||||||
|
"npm ci",
|
||||||
|
"npm run build"
|
||||||
|
],
|
||||||
|
"fileFilters": [
|
||||||
|
"dist/index.js"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user