18 Commits
Author SHA1 Message Date
Renovate 6d24dfcb96 chore(deps): update pnpm/action-setup action to v5
renovate/stability-days Updates have met minimum release age requirement
Lint / pre-commit Linting (push) Successful in 55s
Test / Run Tests (push) Successful in 2m8s
2026-08-10 23:30:43 +00:00
RenovateandRenovate c116597f23 chore(deps): pin pnpm/action-setup action to b906aff (#111)
Lint / pre-commit Linting (push) Successful in 50s
Test / Run Tests (push) Failing after 3m14s
Co-authored-by: Renovate Bot <renovate@horstenkamp.eu>
2026-08-11 00:31:23 +02:00
Philipp 0751b1470b chore: migrate package manager from npm to pnpm (#110)
Lint / pre-commit Linting (push) Successful in 45s
Test / Run Tests (push) Successful in 1m20s
This PR migrates the package manager from `npm` to `pnpm`.

### Changes:
- Replaced `package-lock.json` with `pnpm-lock.yaml` and `pnpm-workspace.yaml`.
- Updated `.gitea/workflows/test.yaml` to include `pnpm/action-setup@v4` and use `pnpm install` / `pnpm test`.
- Updated `renovate.json` to match `pnpm` manager and execute `pnpm install --frozen-lockfile` and `pnpm run build`.
- Updated `.pre-commit-config.yaml` to exclude `pnpm-lock.yaml` from `pretty-format-yaml`.
- Updated documentation in `README.md` and `GEMINI.md`.
- Rebuilt `dist/index.js`.Reviewed-on: #110
2026-08-11 00:20:02 +02:00
RenovateandRenovate 76a263859b chore(deps): update actions/setup-node action to v7 (#108)
Lint / pre-commit Linting (push) Successful in 1m23s
Test / Run Tests (push) Successful in 1m53s
Co-authored-by: Renovate Bot <renovate@horstenkamp.eu>
Co-committed-by: Renovate Bot <renovate@horstenkamp.eu>
2026-07-25 01:50:45 +02:00
RenovateandRenovate 2f32772d0a chore(deps): update actions/setup-node action to v6.5.0 (#107)
Lint / pre-commit Linting (push) Successful in 1m2s
Test / Run Tests (push) Successful in 4m48s
Co-authored-by: Renovate Bot <renovate@horstenkamp.eu>
Co-committed-by: Renovate Bot <renovate@horstenkamp.eu>
2026-07-25 01:06:02 +02:00
RenovateandRenovate 7a447adf45 chore(deps): update actions/setup-python action to v7 (#109)
Lint / pre-commit Linting (push) Successful in 1m0s
Test / Run Tests (push) Successful in 1m35s
Co-authored-by: Renovate Bot <renovate@horstenkamp.eu>
Co-committed-by: Renovate Bot <renovate@horstenkamp.eu>
2026-07-25 01:04:51 +02:00
RenovateandRenovate dcdca8e753 chore(deps): update actions/checkout action to v7.0.1 (#106)
Lint / pre-commit Linting (push) Successful in 58s
Test / Run Tests (push) Successful in 1m36s
Co-authored-by: Renovate Bot <renovate@horstenkamp.eu>
Co-committed-by: Renovate Bot <renovate@horstenkamp.eu>
2026-07-25 00:32:05 +02:00
RenovateandRenovate 9dcad628d4 chore(deps): update pre-commit hook python-jsonschema/check-jsonschema to v0.37.4 (#105)
Lint / pre-commit Linting (push) Successful in 57s
Test / Run Tests (push) Successful in 1m27s
Co-authored-by: Renovate Bot <renovate@horstenkamp.eu>
Co-committed-by: Renovate Bot <renovate@horstenkamp.eu>
2026-07-03 20:53:34 +02:00
RenovateandRenovate c9b5d215f6 chore(deps): update actions/setup-python action to v6.3.0 (#104)
Lint / pre-commit Linting (push) Successful in 58s
Test / Run Tests (push) Successful in 1m36s
Co-authored-by: Renovate Bot <renovate@horstenkamp.eu>
Co-committed-by: Renovate Bot <renovate@horstenkamp.eu>
2026-06-29 02:54:05 +02:00
RenovateandRenovate e8534d7fae chore(deps): update actions/checkout action to v7 (#103)
Lint / pre-commit Linting (push) Successful in 1m9s
Test / Run Tests (push) Successful in 1m32s
Co-authored-by: Renovate Bot <renovate@horstenkamp.eu>
Co-committed-by: Renovate Bot <renovate@horstenkamp.eu>
2026-06-22 02:36:03 +02:00
RenovateandRenovate 873de41ead chore(deps): update pre-commit hook python-jsonschema/check-jsonschema to v0.37.3 (#100)
Lint / pre-commit Linting (push) Successful in 1m13s
Test / Run Tests (push) Successful in 1m52s
Co-authored-by: Renovate Bot <renovate@horstenkamp.eu>
Co-committed-by: Renovate Bot <renovate@horstenkamp.eu>
2026-06-19 03:14:22 +02:00
RenovateandRenovate 36e34e99c5 chore(deps): update actions/checkout action to v6.0.3 (#98)
Lint / pre-commit Linting (push) Successful in 1m3s
Test / Run Tests (push) Successful in 1m26s
Co-authored-by: Renovate Bot <renovate@horstenkamp.eu>
Co-committed-by: Renovate Bot <renovate@horstenkamp.eu>
2026-06-05 16:45:22 +02:00
RenovateandPhilipp 2292b914ad chore(deps): update vitest monorepo to v4.1.8 (#97)
Lint / pre-commit Linting (push) Successful in 54s
Test / Run Tests (push) Successful in 1m21s
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [@vitest/coverage-v8](https://vitest.dev/guide/coverage) ([source](https://github.com/vitest-dev/vitest/tree/HEAD/packages/coverage-v8)) | [`4.1.7` → `4.1.8`](https://renovatebot.com/diffs/npm/@vitest%2fcoverage-v8/4.1.7/4.1.8) | ![age](https://developer.mend.io/api/mc/badges/age/npm/@vitest%2fcoverage-v8/4.1.8?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@vitest%2fcoverage-v8/4.1.7/4.1.8?slim=true) |
| [vitest](https://vitest.dev) ([source](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest)) | [`4.1.7` → `4.1.8`](https://renovatebot.com/diffs/npm/vitest/4.1.7/4.1.8) | ![age](https://developer.mend.io/api/mc/badges/age/npm/vitest/4.1.8?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/vitest/4.1.7/4.1.8?slim=true) |

---

### Release Notes

<details>
<summary>vitest-dev/vitest (@&#8203;vitest/coverage-v8)</summary>

### [`v4.1.8`](https://github.com/vitest-dev/vitest/releases/tag/v4.1.8)

[Compare Source](https://github.com/vitest-dev/vitest/compare/v4.1.7...v4.1.8)

#####    🐞 Bug Fixes

- **browser**:
  - Disable client `cdp` API when `allowWrite/allowExec: false` \[backport to v4]  -  by [@&#8203;hi-ogawa](https://github.com/hi-ogawa) and **Codex** in [#&#8203;10450](https://github.com/vitest-dev/vitest/issues/10450) [<samp>(e4067)</samp>](https://github.com/vitest-dev/vitest/commit/e4067b3b1)
  - Remove orphaned Playwright route when same module is mocked via multiple ids \[backport to v4]  -  by [@&#8203;toxik](https://github.com/toxik) and [@&#8203;Zelys-DFKH](https://github.com/Zelys-DFKH) in [#&#8203;10474](https://github.com/vitest-dev/vitest/issues/10474) [<samp>(675b4)</samp>](https://github.com/vitest-dev/vitest/commit/675b4343f)

#####     [View changes on GitHub](https://github.com/vitest-dev/vitest/compare/v4.1.7...v4.1.8)

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - "on monday,on friday"
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about these updates again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yMDMuMCIsInVwZGF0ZWRJblZlciI6IjQzLjIwMy4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJhdXRvbWF0aW9uIiwibnBtIl19-->

Reviewed-on: https://git.horstenkamp.eu/Screeps/screeps-deploy-action/pulls/97
Reviewed-by: LLMReview <27+autoreview@noreplay.horstenkamp.eu>
Co-authored-by: Renovate Bot <renovate@horstenkamp.eu>
Co-committed-by: Renovate Bot <renovate@horstenkamp.eu>
2026-06-05 13:46:00 +02:00
RenovateandPhilipp bdd0fbfd7e chore(deps): update vitest monorepo to v4.1.7 (#96)
Lint / pre-commit Linting (push) Successful in 51s
Test / Run Tests (push) Successful in 1m21s
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [@vitest/coverage-v8](https://vitest.dev/guide/coverage) ([source](https://github.com/vitest-dev/vitest/tree/HEAD/packages/coverage-v8)) | [`4.1.6` → `4.1.7`](https://renovatebot.com/diffs/npm/@vitest%2fcoverage-v8/4.1.6/4.1.7) | ![age](https://developer.mend.io/api/mc/badges/age/npm/@vitest%2fcoverage-v8/4.1.7?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@vitest%2fcoverage-v8/4.1.6/4.1.7?slim=true) |
| [vitest](https://vitest.dev) ([source](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest)) | [`4.1.6` → `4.1.7`](https://renovatebot.com/diffs/npm/vitest/4.1.6/4.1.7) | ![age](https://developer.mend.io/api/mc/badges/age/npm/vitest/4.1.7?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/vitest/4.1.6/4.1.7?slim=true) |

---

### Release Notes

<details>
<summary>vitest-dev/vitest (@&#8203;vitest/coverage-v8)</summary>

### [`v4.1.7`](https://github.com/vitest-dev/vitest/releases/tag/v4.1.7)

[Compare Source](https://github.com/vitest-dev/vitest/compare/v4.1.6...v4.1.7)

#####    🐞 Bug Fixes

- **runner**: Limit concurrency per task branch in addition to per leaf callbacks (backport)  -  by [@&#8203;hi-ogawa](https://github.com/hi-ogawa) in [#&#8203;10384](https://github.com/vitest-dev/vitest/issues/10384) [<samp>(4f0f2)</samp>](https://github.com/vitest-dev/vitest/commit/4f0f2a1ee)

#####     [View changes on GitHub](https://github.com/vitest-dev/vitest/compare/v4.1.6...v4.1.7)

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - "on monday,on friday"
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about these updates again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xODUuMSIsInVwZGF0ZWRJblZlciI6IjQzLjE4NS4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJhdXRvbWF0aW9uIiwibnBtIl19-->

Reviewed-on: https://git.horstenkamp.eu/Screeps/screeps-deploy-action/pulls/96
Reviewed-by: LLMReview <27+autoreview@noreplay.horstenkamp.eu>
Co-authored-by: Renovate Bot <renovate@horstenkamp.eu>
Co-committed-by: Renovate Bot <renovate@horstenkamp.eu>
2026-05-25 18:38:53 +02:00
RenovateandRenovate df5d81df5c chore(deps): update actions/setup-python action to v6.2.0 (#95)
Lint / pre-commit Linting (push) Successful in 1m2s
Test / Run Tests (push) Successful in 1m17s
Co-authored-by: Renovate Bot <renovate@horstenkamp.eu>
Co-committed-by: Renovate Bot <renovate@horstenkamp.eu>
2026-05-23 01:04:04 +02:00
RenovateandRenovate 0b930f5645 chore(deps): update actions/setup-node action to v6.4.0 (#94)
Lint / pre-commit Linting (push) Successful in 1m1s
Test / Run Tests (push) Successful in 1m23s
Co-authored-by: Renovate Bot <renovate@horstenkamp.eu>
Co-committed-by: Renovate Bot <renovate@horstenkamp.eu>
2026-05-23 00:57:45 +02:00
RenovateandRenovate 67f3f15c2b chore(deps): update actions/checkout action to v6.0.2 (#93)
Lint / pre-commit Linting (push) Successful in 58s
Test / Run Tests (push) Successful in 1m17s
Co-authored-by: Renovate Bot <renovate@horstenkamp.eu>
Co-committed-by: Renovate Bot <renovate@horstenkamp.eu>
2026-05-23 00:47:20 +02:00
Philipp d0a08da728 feat: add rollback_on_failure feature (#88)
Lint / pre-commit Linting (push) Successful in 44s
Test / Run Tests (push) Successful in 1m2s
This PR adds the ability to download existing code before deployment and automatically roll back if the post-deployment monitor detects a failure.

Reviewed-on: #88
Reviewed-by: LLMReview <27+autoreview@noreplay.horstenkamp.eu>
2026-05-17 13:13:58 +02:00
14 changed files with 1602 additions and 2401 deletions
+2 -2
View File
@@ -9,8 +9,8 @@ jobs:
name: pre-commit Linting name: pre-commit Linting
runs-on: pi runs-on: pi
steps: steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
- run: pip install pre-commit - run: pip install pre-commit
shell: bash shell: bash
- name: Pre Commit - name: Pre Commit
+5 -4
View File
@@ -9,11 +9,12 @@ jobs:
name: Run Tests name: Run Tests
runs-on: pi runs-on: pi
steps: steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with: with:
node-version: '24' node-version: '24'
- run: npm install - uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
- run: pnpm install --frozen-lockfile
shell: bash shell: bash
- run: npm test - run: pnpm test
shell: bash shell: bash
+2 -1
View File
@@ -21,6 +21,7 @@ repos:
hooks: hooks:
- id: pretty-format-yaml - id: pretty-format-yaml
args: [--autofix] args: [--autofix]
exclude: ^pnpm-lock\.yaml$
- repo: https://github.com/pre-commit/mirrors-prettier - repo: https://github.com/pre-commit/mirrors-prettier
rev: v4.0.0-alpha.8 rev: v4.0.0-alpha.8
@@ -29,7 +30,7 @@ repos:
types_or: [css, javascript] types_or: [css, javascript]
- repo: https://github.com/python-jsonschema/check-jsonschema - repo: https://github.com/python-jsonschema/check-jsonschema
rev: 0.37.2 rev: 0.37.4
hooks: hooks:
- id: check-renovate - id: check-renovate
- id: check-github-actions - id: check-github-actions
+5 -4
View File
@@ -6,6 +6,7 @@ This repository is maintained by Gemini.
* **Test-Driven Development (TDD):** Wherever possible, Test-Driven Development principles should be followed. Write tests before writing the code they are intended to validate. * **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. * **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.
* **Note for Gemini:** Git commits trigger pre-commit hooks, which can take several seconds (or minutes) to complete. Checking the command status for git commit is only appropriate every 120s.
## Repository Comparison ## Repository Comparison
@@ -18,7 +19,7 @@ This project uses [Vitest](https://vitest.dev/) for testing. The tests are locat
To run the tests locally, use the following command: To run the tests locally, use the following command:
```bash ```bash
npm test pnpm test
``` ```
### Testing Pipeline ### Testing Pipeline
@@ -28,6 +29,6 @@ The tests are automatically run on every push and workflow dispatch using a Gite
The Gitea workflow does the following: The Gitea workflow does the following:
1. Checks out the code. 1. Checks out the code.
2. Sets up Node.js. 2. Sets up Node.js and pnpm.
3. Installs the dependencies using `npm install`. 3. Installs the dependencies using `pnpm install`.
4. Runs the tests using `npm test`. 4. Runs the tests using `pnpm test`.
+3 -3
View File
@@ -84,7 +84,7 @@ This project includes a build step to prepare the action for use.
### Why a Build Step? ### 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: 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. 1. **Zero-configuration at Runtime:** By bundling everything, the action runner doesn't need to run `pnpm install` or `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. 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. 3. **Standard Practice:** This is a recommended practice for JavaScript-based GitHub/Gitea Actions.
@@ -100,8 +100,8 @@ We chose [`@vercel/ncc`](https://github.com/vercel/ncc) as the compiler because:
If you modify `index.js` or change dependencies, you must rebuild the project before committing: If you modify `index.js` or change dependencies, you must rebuild the project before committing:
```bash ```bash
npm install pnpm install
npm run build pnpm run build
``` ```
This will update the `dist/index.js` file, which should be committed to the repository. This will update the `dist/index.js` file, which should be committed to the repository.
+73 -22
View File
@@ -20,6 +20,23 @@ vi.mock("../monitor.js", () => ({
}), }),
})); }));
// Mock screeps-api
vi.mock("screeps-api", () => {
const mockApi = {
auth: vi.fn().mockResolvedValue(),
code: {
get: vi.fn().mockResolvedValue({ ok: 1, modules: { main: "old_code" } }),
set: vi.fn().mockResolvedValue({ ok: 1 }),
},
};
// Use a regular function so it can be called with `new`
return {
ScreepsAPI: vi.fn(function () {
return mockApi;
}),
};
});
import * as core from "@actions/core"; import * as core from "@actions/core";
import { monitorConsole } from "../monitor.js"; import { monitorConsole } from "../monitor.js";
@@ -29,7 +46,9 @@ import {
readReplaceAndWriteFiles, readReplaceAndWriteFiles,
readFilesIntoDict, readFilesIntoDict,
applyOnAction, applyOnAction,
postCode,
} from "../index.js"; } from "../index.js";
import { ScreepsAPI } from "screeps-api";
import fs from "fs"; import fs from "fs";
import path from "path"; import path from "path";
import os from "os"; import os from "os";
@@ -231,31 +250,31 @@ describe("glob functionality", () => {
describe("applyOnAction", () => { describe("applyOnAction", () => {
beforeEach(() => vi.clearAllMocks()); beforeEach(() => vi.clearAllMocks());
it("'ignore' + true → no core call", () => { it("'ignore' + true → no core call, returns false", () => {
applyOnAction("ignore", true, "msg"); expect(applyOnAction("ignore", true, "msg")).toBe(false);
expect(core.warning).not.toHaveBeenCalled(); expect(core.warning).not.toHaveBeenCalled();
expect(core.setFailed).not.toHaveBeenCalled(); expect(core.setFailed).not.toHaveBeenCalled();
}); });
it("'warn' + true → core.warning() called with message", () => { it("'warn' + true → core.warning() called with message, returns false", () => {
applyOnAction("warn", true, "boom"); expect(applyOnAction("warn", true, "boom")).toBe(false);
expect(core.warning).toHaveBeenCalledWith("boom"); expect(core.warning).toHaveBeenCalledWith("boom");
expect(core.setFailed).not.toHaveBeenCalled(); expect(core.setFailed).not.toHaveBeenCalled();
}); });
it("'fail' + true → core.setFailed() called with message", () => { it("'fail' + true → core.setFailed() called with message, returns true", () => {
applyOnAction("fail", true, "boom"); expect(applyOnAction("fail", true, "boom")).toBe(true);
expect(core.setFailed).toHaveBeenCalledWith("boom"); expect(core.setFailed).toHaveBeenCalledWith("boom");
expect(core.warning).not.toHaveBeenCalled(); expect(core.warning).not.toHaveBeenCalled();
}); });
it("'fail' + false → no core call", () => { it("'fail' + false → no core call, returns false", () => {
applyOnAction("fail", false, "boom"); expect(applyOnAction("fail", false, "boom")).toBe(false);
expect(core.setFailed).not.toHaveBeenCalled(); expect(core.setFailed).not.toHaveBeenCalled();
}); });
it("'warn' + false → no core call", () => { it("'warn' + false → no core call, returns false", () => {
applyOnAction("warn", false, "msg"); expect(applyOnAction("warn", false, "msg")).toBe(false);
expect(core.warning).not.toHaveBeenCalled(); expect(core.warning).not.toHaveBeenCalled();
}); });
}); });
@@ -270,28 +289,60 @@ describe("postCode — monitor wiring", () => {
// Default core mocks // Default core mocks
core.getInput.mockImplementation((name) => { core.getInput.mockImplementation((name) => {
if (name === "monitor") return "0"; if (name === "monitor") return "0";
if (name === "token") return "test-token";
if (name === "branch") return "default";
if (name === "on_traceback") return "fail";
return ""; return "";
}); });
core.getBooleanInput.mockReturnValue(false); core.getBooleanInput.mockImplementation((name) => {
if (name === "rollback_on_failure") return false;
return false;
});
}); });
it("does not call monitorConsole when monitor=0 (default)", async () => { it("does not call monitorConsole when monitor=0 (default)", async () => {
// We need to mock the rest of postCode to not fail before it hits the monitor block // We just run postCode with monitor=0 and verify monitorConsole is not called.
// This is a bit complex as postCode is large, but we can mock the inputs to exit early or mock the API await postCode();
// Actually, I'll just check if monitorConsole is called. expect(monitorConsole).not.toHaveBeenCalled();
});
// For this test, I'll make validateAuthentication fail so it returns early but after input check it("rolls back to previous code when monitor detects a failure and rollback_on_failure is true", async () => {
// Setup inputs for monitor and rollback
core.getInput.mockImplementation((name) => { core.getInput.mockImplementation((name) => {
if (name === "monitor") return "0"; if (name === "monitor") return "10";
if (name === "token") return "test-token";
if (name === "branch") return "default";
if (name === "on_traceback") return "fail";
return ""; return "";
}); });
core.getBooleanInput.mockImplementation((name) => {
if (name === "rollback_on_failure") return true;
return false;
});
// We'll just run a partial check or rely on the monitor unit tests for depth // Simulate a failure in monitorConsole
// The wiring in index.js is: monitorConsole.mockResolvedValueOnce({
// const monitorTicks = parseInt(core.getInput("monitor") || "0", 10); sawTraceback: true, // Should trigger "fail" due to on_traceback=fail
// if (monitorTicks > 0) { ... } sawErrorLog: false,
sawWarningLog: false,
});
// Testing the logic inside index.js directly by calling postCode would require full environment mock. await postCode();
// I'll stick to the applyOnAction unit tests and rely on monitor.test.js for the heavy lifting.
// Verify rollback was performed
const mockApiInstance = new ScreepsAPI();
// `code.set` should be called twice:
// 1st time: uploading the new files
// 2nd time: rolling back to oldCode
expect(mockApiInstance.code.set).toHaveBeenCalledTimes(2);
expect(mockApiInstance.code.set).toHaveBeenNthCalledWith(2, "default", {
main: "old_code",
});
// Verify it called core.setFailed due to traceback
expect(core.setFailed).toHaveBeenCalledWith(
"Screeps console: traceback detected",
);
}); });
}); });
+4
View File
@@ -67,6 +67,10 @@ inputs:
description: 'Print a progress update every N ticks when log_to_file=true (default: 10).' description: 'Print a progress update every N ticks when log_to_file=true (default: 10).'
required: false required: false
default: '10' default: '10'
rollback_on_failure:
description: 'Automatically rollback to previous code if the monitor detects failures. Requires monitor > 0. (default: false)'
required: false
default: 'false'
outputs: outputs:
saw_traceback: saw_traceback:
description: true if a JS traceback was detected during monitoring. description: true if a JS traceback was detected during monitoring.
+8 -8
View File
File diff suppressed because one or more lines are too long
+80 -23
View File
@@ -118,17 +118,19 @@ export function validateAuthentication(token, username, password) {
* @param {'ignore'|'warn'|'fail'} action * @param {'ignore'|'warn'|'fail'} action
* @param {boolean} flag - Only acts when true * @param {boolean} flag - Only acts when true
* @param {string} message - Passed to core.warning / core.setFailed * @param {string} message - Passed to core.warning / core.setFailed
* @returns {boolean} - Returns true if the action was 'fail' and the flag was true.
*/ */
export function applyOnAction(action, flag, message) { export function applyOnAction(action, flag, message) {
if (!flag) return; if (!flag) return false;
if (action === "warn") { if (action === "warn") {
core.warning(message); core.warning(message);
return; return false;
} }
if (action === "fail") { if (action === "fail") {
core.setFailed(message); core.setFailed(message);
return true;
} }
// 'ignore' → no-op return false;
} }
/** /**
@@ -179,33 +181,72 @@ export async function postCode() {
return; return;
} }
let api = new ScreepsAPI(login_arguments); let api = new ScreepsAPI(login_arguments);
if (token) {
if (!token) {
core.info(`Logging in as user ${username}`);
try {
await api.auth(username, password, login_arguments);
} catch (err) {
core.error(`Authentication error: ${err}`);
throw err;
}
}
let oldCode = null;
let rollbackOnFailure = false;
try {
rollbackOnFailure = core.getBooleanInput("rollback_on_failure");
} catch (e) {
// getBooleanInput throws if not 'true' or 'false', ignore
}
if (rollbackOnFailure) {
core.info(
`Downloading existing code from branch ${branch} for potential rollback...`,
);
try {
const getResponse = await api.code.get(branch);
if (getResponse && getResponse.ok && getResponse.modules) {
oldCode = getResponse.modules;
core.info(
`Successfully downloaded existing code (modules: ${Object.keys(oldCode).join(", ")})`,
);
} else {
core.setFailed(
`Failed to download existing code, but rollback_on_failure is enabled. Aborting deployment.`,
);
return;
}
} catch (err) {
core.setFailed(
`Error downloading existing code: ${err.message}. Aborting deployment.`,
);
return;
}
}
try {
const response = await api.code.set(branch, files_to_push); const response = await api.code.set(branch, files_to_push);
core.info(JSON.stringify(response, null, 2)); core.info(JSON.stringify(response, null, 2));
core.info(`Code set successfully to ${branch}`); core.info(`Code set successfully to ${branch}`);
} else { } catch (err) {
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(() => {
core.info(`Code set successfully to ${branch}`);
})
.catch((err) => {
core.error(`Upload error: ${err}`); core.error(`Upload error: ${err}`);
throw err; throw err;
});
} }
// Console monitoring (optional) // Console monitoring (optional)
const monitorTicks = parseInt(core.getInput("monitor") || "0", 10); const monitorTicks = parseInt(core.getInput("monitor") || "0", 10);
if (monitorTicks > 0) { if (monitorTicks > 0) {
const onTraceback = core.getInput("on_traceback") || "fail";
const onErrorLog = core.getInput("on_error_log") || "warn";
const onWarningLog = core.getInput("on_warning_log") || "ignore";
const result = await monitorConsole(api, { const result = await monitorConsole(api, {
monitor: monitorTicks, monitor: monitorTicks,
logToFile: core.getBooleanInput("log_to_file"), logToFile: core.getBooleanInput("log_to_file"),
onTraceback: core.getInput("on_traceback") || "fail", onTraceback,
onErrorLog: core.getInput("on_error_log") || "warn", onErrorLog,
onWarningLog: core.getInput("on_warning_log") || "ignore", onWarningLog,
monitorInterval: parseInt(core.getInput("monitor_interval") || "10", 10), monitorInterval: parseInt(core.getInput("monitor_interval") || "10", 10),
hostname, hostname,
shard: core.getInput("shard") || undefined, shard: core.getInput("shard") || undefined,
@@ -215,21 +256,37 @@ export async function postCode() {
core.setOutput("saw_error_log", String(result.sawErrorLog)); core.setOutput("saw_error_log", String(result.sawErrorLog));
core.setOutput("saw_warning_log", String(result.sawWarningLog)); core.setOutput("saw_warning_log", String(result.sawWarningLog));
applyOnAction( const fail1 = applyOnAction(
core.getInput("on_traceback"), onTraceback,
result.sawTraceback, result.sawTraceback,
"Screeps console: traceback detected", "Screeps console: traceback detected",
); );
applyOnAction( const fail2 = applyOnAction(
core.getInput("on_error_log"), onErrorLog,
result.sawErrorLog, result.sawErrorLog,
"Screeps console: error log output detected", "Screeps console: error log output detected",
); );
applyOnAction( const fail3 = applyOnAction(
core.getInput("on_warning_log"), onWarningLog,
result.sawWarningLog, result.sawWarningLog,
"Screeps console: warning log output detected", "Screeps console: warning log output detected",
); );
const shouldFail = fail1 || fail2 || fail3;
if (shouldFail && rollbackOnFailure && oldCode) {
core.info(
"Action failed based on monitor configuration. Rolling back to previous code...",
);
try {
await api.code.set(branch, oldCode);
core.info(
`Successfully rolled back to previous code on branch ${branch}.`,
);
} catch (err) {
core.error(`Rollback failed: ${err}`);
}
}
} }
} }
-2326
View File
File diff suppressed because it is too large Load Diff
+4 -3
View File
@@ -3,6 +3,7 @@
"version": "0.2.1", "version": "0.2.1",
"description": "Deploys screeps code to the official game or a private server.", "description": "Deploys screeps code to the official game or a private server.",
"type": "module", "type": "module",
"packageManager": "pnpm@11.21.0",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"start": "node index.js", "start": "node index.js",
@@ -16,8 +17,8 @@
"screeps-api": "^1.7.2" "screeps-api": "^1.7.2"
}, },
"devDependencies": { "devDependencies": {
"@vercel/ncc": "^0.38.4", "@vercel/ncc": "^0.44.1",
"@vitest/coverage-v8": "^4.0.16", "@vitest/coverage-v8": "^4.1.10",
"vitest": "^4.0.16" "vitest": "^4.1.10"
} }
} }
+1408
View File
File diff suppressed because it is too large Load Diff
+3
View File
@@ -0,0 +1,3 @@
allowBuilds:
bufferutil: true
utf-8-validate: true
+3 -3
View File
@@ -8,12 +8,12 @@
"packageRules": [ "packageRules": [
{ {
"matchManagers": [ "matchManagers": [
"npm" "pnpm"
], ],
"postUpgradeTasks": { "postUpgradeTasks": {
"commands": [ "commands": [
"npm ci", "pnpm install --frozen-lockfile",
"npm run build" "pnpm run build"
], ],
"fileFilters": [ "fileFilters": [
"dist/index.js" "dist/index.js"