From 42c9c5358123429f13f96deaa6d328ada171300b Mon Sep 17 00:00:00 2001
From: Shane <6071159+smashedr@users.noreply.github.com>
Date: Sat, 8 Nov 2025 22:53:58 -0800
Subject: [PATCH 1/2] Updates for v2 and node24
---
.github/workflows/check-build.yaml | 34 +---
.github/workflows/labeler.yaml | 6 +-
.github/workflows/lint.yaml | 7 +-
.github/workflows/mirror.yaml | 2 +-
.github/workflows/pull.yaml | 8 +-
.github/workflows/release.yaml | 9 +-
.github/workflows/tags.yaml | 2 +-
.github/workflows/test.yaml | 8 +
.prettierignore | 1 +
README.md | 5 +-
action.yml | 4 +-
dist/index.js | 294 ++++++++++++++---------------
package-lock.json | 2 +
package.json | 2 +-
src/index.js | 290 ++++++++++++++--------------
15 files changed, 339 insertions(+), 335 deletions(-)
diff --git a/.github/workflows/check-build.yaml b/.github/workflows/check-build.yaml
index 510ad80..f835fca 100644
--- a/.github/workflows/check-build.yaml
+++ b/.github/workflows/check-build.yaml
@@ -1,7 +1,14 @@
name: "Check Build"
on:
+ push:
+ branches: [master]
pull_request_target:
+ paths:
+ - "dist/**"
+ - "src/**"
+ - "package*.json"
+ - "rollup.config.*"
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@@ -24,24 +31,12 @@ jobs:
GITHUB_CTX: ${{ toJSON(github) }}
run: echo "$GITHUB_CTX"
- - name: "Check Changed Files"
- id: changed
- uses: tj-actions/changed-files@24d32ffd492484c1d75e0c0b894501ddb9d30d62 # v47
- with:
- files: |
- dist/**
- src/**
- package*.json
- rollup.config.*
-
- name: "Checkout Pull"
- if: ${{ steps.changed.outputs.any_changed == 'true' }}
uses: actions/checkout@v5
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: "Debug"
- if: ${{ steps.changed.outputs.any_changed == 'true' }}
continue-on-error: true
run: |
echo "::group::ls"
@@ -51,18 +46,5 @@ jobs:
tree .
echo "::endgroup::"
- #- name: "Setup Node"
- # if: ${{ steps.changed.outputs.any_changed == 'true' }}
- # uses: actions/setup-node@v6
- # with:
- # node-version: 24
- #
- #- name: "Install"
- # if: ${{ steps.changed.outputs.any_changed == 'true' }}
- # id: install
- # run: |
- # npm ci
-
- name: "Check Build Action"
- if: ${{ steps.changed.outputs.any_changed == 'true' }}
- uses: cssnr/check-build-action@v1
+ uses: cssnr/check-build-action@latest
diff --git a/.github/workflows/labeler.yaml b/.github/workflows/labeler.yaml
index eb36453..2f3c44d 100644
--- a/.github/workflows/labeler.yaml
+++ b/.github/workflows/labeler.yaml
@@ -3,6 +3,10 @@ name: "PR Labeler"
on:
pull_request_target:
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
jobs:
labeler:
name: "Labeler"
@@ -37,7 +41,7 @@ jobs:
- name: "Label Creator"
continue-on-error: true
- uses: cssnr/label-creator-action@v1
+ uses: cssnr/label-creator-action@latest
with:
file: .configs/labels/labels.yaml
diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml
index 4792273..306a104 100644
--- a/.github/workflows/lint.yaml
+++ b/.github/workflows/lint.yaml
@@ -13,6 +13,7 @@ concurrency:
jobs:
lint:
name: "Lint"
+ if: ${{ !contains(github.event.head_commit.message, '#nolint') }}
runs-on: ubuntu-latest
timeout-minutes: 5
@@ -75,11 +76,7 @@ jobs:
run: |
yq -e '.runs.main | test("^dist/")' action.yml
- #- name: "Check Build Action"
- # if: ${{ !cancelled() }}
- # uses: cssnr/check-build-action@v1
-
- name: "ESLint Annotate"
if: ${{ !cancelled() && steps.eslint.outcome != 'success' }}
continue-on-error: true
- uses: ataylorme/eslint-annotate-action@v3
+ uses: ataylorme/eslint-annotate-action@d57a1193d4c59cbfbf3f86c271f42612f9dbd9e9 # 3.0.0
diff --git a/.github/workflows/mirror.yaml b/.github/workflows/mirror.yaml
index 00bdae6..db82412 100644
--- a/.github/workflows/mirror.yaml
+++ b/.github/workflows/mirror.yaml
@@ -23,7 +23,7 @@ jobs:
fetch-depth: 0
- name: "Mirror to Codeberg"
- uses: cssnr/mirror-repository-action@v1
+ uses: cssnr/mirror-repository-action@latest
with:
host: https://codeberg.org
create: true
diff --git a/.github/workflows/pull.yaml b/.github/workflows/pull.yaml
index da86b80..3fe1eec 100644
--- a/.github/workflows/pull.yaml
+++ b/.github/workflows/pull.yaml
@@ -41,4 +41,10 @@ jobs:
- name: "NPM Outdated Check"
continue-on-error: true
- uses: cssnr/npm-outdated-action@master
+ uses: cssnr/npm-outdated-action@latest
+
+ - name: "Actions Up"
+ continue-on-error: true
+ uses: cssnr/actions-up-action@latest
+ with:
+ exclude: "cssnr/.*,actions/.*"
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index c37a857..6177006 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -16,7 +16,9 @@ jobs:
steps:
- name: "Update Tags"
id: tags
- uses: cssnr/update-version-tags-action@v1
+ uses: cssnr/update-version-tags-action@latest
+ with:
+ tags: "latest"
- name: "Debug Tags"
continue-on-error: true
@@ -30,13 +32,14 @@ jobs:
with:
tags: ${{ steps.tags.outputs.tags }}
location: tail
+ type: "actions"
- name: "Package Changelog Action"
continue-on-error: true
- uses: cssnr/package-changelog-action@v1
+ uses: cssnr/package-changelog-action@latest
- name: "Send Failure Notification"
if: ${{ failure() }}
- uses: sarisia/actions-status-discord@v1
+ uses: sarisia/actions-status-discord@b8381b25576cb341b2af39926ab42c5056cc44ed # v1.15.5
with:
webhook: ${{ secrets.DISCORD_WEBHOOK }}
diff --git a/.github/workflows/tags.yaml b/.github/workflows/tags.yaml
index 38b5c74..a2ee80c 100644
--- a/.github/workflows/tags.yaml
+++ b/.github/workflows/tags.yaml
@@ -18,7 +18,7 @@ jobs:
steps:
- name: "Update Tags"
- uses: cssnr/update-version-tags-action@v1
+ uses: cssnr/update-version-tags-action@latest
with:
tag: ${{ inputs.tag }}
token: ${{ secrets.GH_PAT }}
diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index 4f275e0..5ca23e2 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -1,5 +1,7 @@
name: "Test"
+# actions-up-ignore-file
+
on:
workflow_dispatch:
schedule:
@@ -35,6 +37,12 @@ jobs:
GITHUB_CTX: ${{ toJSON(github) }}
run: echo "$GITHUB_CTX"
+ - name: "Debug Environment"
+ if: ${{ !github.event.act }}
+ continue-on-error: true
+ run: |
+ env
+
- name: "Write YAML"
id: yaml-action
uses: teunmooij/yaml@v1
diff --git a/.prettierignore b/.prettierignore
index cd26c30..b01fca6 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -14,4 +14,5 @@ node_modules/
.github/disabled/
# Files
+.github/PULL_REQUEST_TEMPLATE/
.github/pull_request_template.md
diff --git a/README.md b/README.md
index cf879ac..5aa6724 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,7 @@
[](https://github.com/cssnr/portainer-stack-deploy-action/releases)
[](https://github.com/cssnr/portainer-stack-deploy-action/releases/latest)
[](https://github.com/cssnr/portainer-stack-deploy-action/blob/master/src)
+[](https://github.com/cssnr/portainer-stack-deploy-action/blob/master/action.yml)
[](https://github.com/cssnr/portainer-stack-deploy-action/actions/workflows/release.yaml)
[](https://github.com/cssnr/portainer-stack-deploy-action/actions/workflows/test.yaml)
[](https://github.com/cssnr/portainer-stack-deploy-action/actions/workflows/lint.yaml)
@@ -720,9 +721,9 @@ These actions are not published on the Marketplace, but may be useful.
These are basic action templates that I use for creating new actions.
- [js-test-action](https://github.com/smashedr/js-test-action?tab=readme-ov-file#readme) - JavaScript
-- [py-test-action](https://github.com/smashedr/py-test-action?tab=readme-ov-file#readme) - Python
- [ts-test-action](https://github.com/smashedr/ts-test-action?tab=readme-ov-file#readme) - TypeScript
-- [docker-test-action](https://github.com/smashedr/docker-test-action?tab=readme-ov-file#readme) - Docker Image
+- [py-test-action](https://github.com/smashedr/py-test-action?tab=readme-ov-file#readme) - Python (Dockerfile)
+- [docker-test-action](https://github.com/smashedr/docker-test-action?tab=readme-ov-file#readme) - Docker (Image)
Note: The `docker-test-action` builds, runs and pushes images to [GitHub Container Registry](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry).
diff --git a/action.yml b/action.yml
index 66979f5..e70f8d9 100644
--- a/action.yml
+++ b/action.yml
@@ -56,7 +56,7 @@ inputs:
password:
description: "Repository Password"
fs_path:
- description: "Relative path volume in host (only available in Portainer BE)"
+ description: "Relative path volume in host (Portainer BE)"
headers:
description: "Custom Headers JSON"
summary:
@@ -72,5 +72,5 @@ outputs:
description: "Endpoint ID"
runs:
- using: "node20"
+ using: "node24"
main: "dist/index.js"
diff --git a/dist/index.js b/dist/index.js
index efc8440..7b2b69f 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -42212,168 +42212,162 @@ const yaml = __nccwpck_require__(4281)
const Portainer = __nccwpck_require__(1055)
-;(async () => {
- try {
- core.info('🏳️ Portainer Stack Deploy Action')
+async function main() /* NOSONAR */ {
+ core.info('🏳️ Portainer Stack Deploy Action')
- // Parse Inputs
- const inputs = getInputs()
- core.startGroup('Parsed Inputs')
- console.log('inputs:', inputs)
- core.endGroup() // Inputs
+ // Parse Inputs
+ const inputs = getInputs()
+ core.startGroup('Parsed Inputs')
+ console.log('inputs:', inputs)
+ core.endGroup() // Inputs
- if (!['repo', 'file'].includes(inputs.type)) {
- core.setFailed(`Unknown type: ${inputs.type}. Values: [repo, file]`)
- return
- }
+ if (!['repo', 'file'].includes(inputs.type)) {
+ core.setFailed(`Unknown type: ${inputs.type}. Values: [repo, file]`)
+ return
+ }
- // Check Portainer
- const headers = parseData(inputs.headers)
- // console.log('headers:', headers)
- const portainer = new Portainer(inputs.url, inputs.token, headers)
- const version = await portainer.getVersion()
- const versionString = `${version.ServerVersion} ${version.VersionSupport} ${version.ServerEdition}`
- core.startGroup(`Portainer Version: \u001b[34m${versionString}`)
- delete version.Runtime
- console.log(version)
- core.endGroup() // Portainer Version
-
- if (inputs.fs_path) {
- if (version.ServerEdition !== 'EE') {
- core.setFailed('Relative path only supported in Portainer EE!')
- return
- }
+ // Check Portainer
+ const headers = parseData(inputs.headers)
+ // console.log('headers:', headers)
+ const portainer = new Portainer(inputs.url, inputs.token, headers)
+ const version = await portainer.getVersion()
+ const versionString = `${version.ServerVersion} ${version.VersionSupport} ${version.ServerEdition}`
+ core.startGroup(`Portainer Version: \u001b[34m${versionString}`)
+ delete version.Runtime
+ console.log(version)
+ core.endGroup() // Portainer Version
+
+ if (inputs.fs_path) {
+ if (version.ServerEdition !== 'EE') {
+ core.setFailed('Relative path only supported in Portainer EE!')
+ return
}
+ }
- // Set Variables
- let endpointID = Number.parseInt(inputs.endpoint)
+ // Set Variables
+ let endpointID = Number.parseInt(inputs.endpoint)
+ if (!endpointID) {
+ const endpoints = await portainer.getEndpoints()
+ // console.log('endpoints:', endpoints)
+ endpointID = endpoints[0]?.Id
if (!endpointID) {
- const endpoints = await portainer.getEndpoints()
- // console.log('endpoints:', endpoints)
- endpointID = endpoints[0]?.Id
- if (!endpointID) {
- return core.setFailed('No Endpoints Found!')
- }
+ return core.setFailed('No Endpoints Found!')
}
- core.info(`endpointID: \u001b[36m${endpointID}`)
+ }
+ core.info(`endpointID: \u001b[36m${endpointID}`)
- let swarmID = null
- if (!inputs.standalone) {
- const swarm = await portainer.getSwarm(endpointID)
- // console.log('swarm:', swarm)
- swarmID = swarm.ID
- }
- core.info(`swarmID: \u001b[36m${swarmID}`)
+ let swarmID = null
+ if (!inputs.standalone) {
+ const swarm = await portainer.getSwarm(endpointID)
+ // console.log('swarm:', swarm)
+ swarmID = swarm.ID
+ }
+ core.info(`swarmID: \u001b[36m${swarmID}`)
- // Get Stack
- const stacks = await portainer.getStacks()
- // console.log('stacks:', stacks)
- let stack = stacks.find(
- (item) => item.Name === inputs.name && item.EndpointId === endpointID
- )
- // console.log('stack:', stack)
- let stackID = stack?.Id
- core.info(`stackID: \u001b[36m${stackID}`)
-
- // Update Environment
- const env = getEnv(inputs, stack)
-
- // Perform Deploy
- if (inputs.type === 'repo') {
- core.info('🌐 Performing Repository Deployment')
- const repositoryAuthentication = !!(inputs.username || inputs.password)
- if (stackID) {
- core.info(`Stack Found - Updating Stack ID: ${stack.Id}`)
- const body = {
- env,
- prune: inputs.prune,
- pullImage: inputs.pull,
- repositoryReferenceName: inputs.ref,
- repositoryAuthentication,
- repositoryPassword: inputs.password,
- repositoryUsername: inputs.username,
- }
- // console.log('body:', body)
- stack = await portainer.updateStackRepo(stackID, endpointID, body)
- // console.log('stack:', stack)
- core.info(`Updated Stack ${stack.Id}: ${stack.Name}`)
- } else {
- core.info('Stack NOT Found - Deploying NEW Stack')
- const body = {
- name: inputs.name,
- swarmID,
- repositoryURL: inputs.repo,
- composeFile: inputs.file,
- env,
- tlsskipVerify: inputs.tlsskip,
- repositoryReferenceName: inputs.ref,
- repositoryAuthentication,
- repositoryPassword: inputs.password,
- repositoryUsername: inputs.username,
- ...(inputs.fs_path && {
- supportRelativePath: true,
- fileSystemPath: inputs.fs_path,
- }),
- }
- // console.log('body:', body)
- stack = await portainer.createStackRepo(endpointID, body)
- // console.log('stack:', stack)
- core.info(`Deployed Stack: ${stack.Id}: ${stack.Name}`)
+ // Get Stack
+ const stacks = await portainer.getStacks()
+ // console.log('stacks:', stacks)
+ let stack = stacks.find(
+ (item) => item.Name === inputs.name && item.EndpointId === endpointID
+ )
+ // console.log('stack:', stack)
+ let stackID = stack?.Id
+ core.info(`stackID: \u001b[36m${stackID}`)
+
+ // Update Environment
+ const env = getEnv(inputs, stack)
+
+ // Perform Deploy
+ if (inputs.type === 'repo') {
+ core.info('🌐 Performing Repository Deployment')
+ const repositoryAuthentication = !!(inputs.username || inputs.password)
+ if (stackID) {
+ core.info(`Stack Found - Updating Stack ID: ${stack.Id}`)
+ const body = {
+ env,
+ prune: inputs.prune,
+ pullImage: inputs.pull,
+ repositoryReferenceName: inputs.ref,
+ repositoryAuthentication,
+ repositoryPassword: inputs.password,
+ repositoryUsername: inputs.username,
}
- } else if (inputs.type === 'file') {
- core.info('📄 Performing Stack File Deployment')
- const stackFileContent = fs.readFileSync(inputs.file, 'utf-8')
- if (stackID) {
- core.info(`Stack Found - Updating Stack ID: ${stackID}`)
- const body = {
- env,
- prune: inputs.prune,
- pullImage: inputs.pull,
- stackFileContent,
- }
- // console.log('body:', body)
- stack = await portainer.updateStackString(stackID, endpointID, body)
- // console.log('stack:', stack)
- core.info(`Updated Stack ${stack.Id}: ${stack.Name}`)
- } else {
- core.info('Stack NOT Found - Deploying NEW Stack')
- const body = {
- name: inputs.name,
- swarmID,
- stackFileContent,
- env,
- }
- // console.log('body:', body)
- stack = await portainer.createStackString(endpointID, body)
- // console.log('stack:', stack)
- core.info(`Deployed Stack: ${stack.Id}: ${stack.Name}`)
+ // console.log('body:', body)
+ stack = await portainer.updateStackRepo(stackID, endpointID, body)
+ // console.log('stack:', stack)
+ core.info(`Updated Stack ${stack.Id}: ${stack.Name}`)
+ } else {
+ core.info('Stack NOT Found - Deploying NEW Stack')
+ const body = {
+ name: inputs.name,
+ swarmID,
+ repositoryURL: inputs.repo,
+ composeFile: inputs.file,
+ env,
+ tlsskipVerify: inputs.tlsskip,
+ repositoryReferenceName: inputs.ref,
+ repositoryAuthentication,
+ repositoryPassword: inputs.password,
+ repositoryUsername: inputs.username,
+ ...(inputs.fs_path && {
+ supportRelativePath: true,
+ fileSystemPath: inputs.fs_path,
+ }),
}
+ // console.log('body:', body)
+ stack = await portainer.createStackRepo(endpointID, body)
+ // console.log('stack:', stack)
+ core.info(`Deployed Stack: ${stack.Id}: ${stack.Name}`)
+ }
+ } else if (inputs.type === 'file') {
+ core.info('📄 Performing Stack File Deployment')
+ const stackFileContent = fs.readFileSync(inputs.file, 'utf-8')
+ if (stackID) {
+ core.info(`Stack Found - Updating Stack ID: ${stackID}`)
+ const body = {
+ env,
+ prune: inputs.prune,
+ pullImage: inputs.pull,
+ stackFileContent,
+ }
+ // console.log('body:', body)
+ stack = await portainer.updateStackString(stackID, endpointID, body)
+ // console.log('stack:', stack)
+ core.info(`Updated Stack ${stack.Id}: ${stack.Name}`)
+ } else {
+ core.info('Stack NOT Found - Deploying NEW Stack')
+ const body = {
+ name: inputs.name,
+ swarmID,
+ stackFileContent,
+ env,
+ }
+ // console.log('body:', body)
+ stack = await portainer.createStackString(endpointID, body)
+ // console.log('stack:', stack)
+ core.info(`Deployed Stack: ${stack.Id}: ${stack.Name}`)
}
+ }
- // Set Outputs
- core.info('📩 Setting Outputs')
- core.setOutput('stackID', stack.Id)
- core.setOutput('swarmID', swarmID)
- core.setOutput('endpointID', endpointID)
+ // Set Outputs
+ core.info('📩 Setting Outputs')
+ core.setOutput('stackID', stack.Id)
+ core.setOutput('swarmID', swarmID)
+ core.setOutput('endpointID', endpointID)
- // Summary
- if (inputs.summary) {
- core.info('📝 Writing Job Summary')
- try {
- await addSummary(inputs, stack)
- } catch (e) {
- console.log(e)
- core.error(`Error writing Job Summary ${e.message}`)
- }
+ // Summary
+ if (inputs.summary) {
+ core.info('📝 Writing Job Summary')
+ try {
+ await addSummary(inputs, stack)
+ } catch (e) {
+ console.log(e)
+ core.error(`Error writing Job Summary ${e.message}`)
}
-
- core.info('✅ \u001b[32;1mFinished Success')
- } catch (e) {
- core.debug(e)
- console.log('response:', e.response?.data)
- core.setFailed(e.message)
}
-})()
+
+ core.info('✅ \u001b[32;1mFinished Success')
+}
/**
* @function getEnv
@@ -42472,7 +42466,7 @@ async function addSummary(inputs, stack) {
}
/**
- * Parse Data from Input
+ * Parse JSON/YAML Data from a String
* @param {string} data
* @return {object}
*/
@@ -42545,6 +42539,12 @@ function getInputs() {
}
}
+main().catch((e) => {
+ core.debug(e)
+ core.info(e.message)
+ core.setFailed(e.message)
+})
+
module.exports = __webpack_exports__;
/******/ })()
;
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index c2da72b..d425a6b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -288,6 +288,7 @@
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"license": "MIT",
+ "peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -597,6 +598,7 @@
"integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
diff --git a/package.json b/package.json
index 22e7842..9956787 100644
--- a/package.json
+++ b/package.json
@@ -3,7 +3,7 @@
"private": true,
"main": "dist/index.js",
"scripts": {
- "build": "ncc build src/index.js",
+ "build": "npx ncc build src/index.js",
"build:watch": "npm run build -- --watch",
"lint": "npx eslint src",
"lint:report": "npm run lint -- --output-file eslint_report.json --format json",
diff --git a/src/index.js b/src/index.js
index 20cd1cc..514cba4 100644
--- a/src/index.js
+++ b/src/index.js
@@ -3,170 +3,164 @@ const fs = require('node:fs')
const dotenv = require('dotenv')
const yaml = require('js-yaml')
-const Portainer = require('./portainer')
+const Portainer = require('./portainer.js')
-;(async () => {
- try {
- core.info('🏳️ Portainer Stack Deploy Action')
+async function main() /* NOSONAR */ {
+ core.info('🏳️ Portainer Stack Deploy Action')
- // Parse Inputs
- const inputs = getInputs()
- core.startGroup('Parsed Inputs')
- console.log('inputs:', inputs)
- core.endGroup() // Inputs
+ // Parse Inputs
+ const inputs = getInputs()
+ core.startGroup('Parsed Inputs')
+ console.log('inputs:', inputs)
+ core.endGroup() // Inputs
- if (!['repo', 'file'].includes(inputs.type)) {
- core.setFailed(`Unknown type: ${inputs.type}. Values: [repo, file]`)
- return
- }
+ if (!['repo', 'file'].includes(inputs.type)) {
+ core.setFailed(`Unknown type: ${inputs.type}. Values: [repo, file]`)
+ return
+ }
- // Check Portainer
- const headers = parseData(inputs.headers)
- // console.log('headers:', headers)
- const portainer = new Portainer(inputs.url, inputs.token, headers)
- const version = await portainer.getVersion()
- const versionString = `${version.ServerVersion} ${version.VersionSupport} ${version.ServerEdition}`
- core.startGroup(`Portainer Version: \u001b[34m${versionString}`)
- delete version.Runtime
- console.log(version)
- core.endGroup() // Portainer Version
+ // Check Portainer
+ const headers = parseData(inputs.headers)
+ // console.log('headers:', headers)
+ const portainer = new Portainer(inputs.url, inputs.token, headers)
+ const version = await portainer.getVersion()
+ const versionString = `${version.ServerVersion} ${version.VersionSupport} ${version.ServerEdition}`
+ core.startGroup(`Portainer Version: \u001b[34m${versionString}`)
+ delete version.Runtime
+ console.log(version)
+ core.endGroup() // Portainer Version
- if (inputs.fs_path) {
- if (version.ServerEdition !== 'EE') {
- core.setFailed('Relative path only supported in Portainer EE!')
- return
- }
+ if (inputs.fs_path) {
+ if (version.ServerEdition !== 'EE') {
+ core.setFailed('Relative path only supported in Portainer EE!')
+ return
}
+ }
- // Set Variables
- let endpointID = Number.parseInt(inputs.endpoint)
+ // Set Variables
+ let endpointID = Number.parseInt(inputs.endpoint)
+ if (!endpointID) {
+ const endpoints = await portainer.getEndpoints()
+ // console.log('endpoints:', endpoints)
+ endpointID = endpoints[0]?.Id
if (!endpointID) {
- const endpoints = await portainer.getEndpoints()
- // console.log('endpoints:', endpoints)
- endpointID = endpoints[0]?.Id
- if (!endpointID) {
- return core.setFailed('No Endpoints Found!')
- }
+ return core.setFailed('No Endpoints Found!')
}
- core.info(`endpointID: \u001b[36m${endpointID}`)
+ }
+ core.info(`endpointID: \u001b[36m${endpointID}`)
- let swarmID = null
- if (!inputs.standalone) {
- const swarm = await portainer.getSwarm(endpointID)
- // console.log('swarm:', swarm)
- swarmID = swarm.ID
- }
- core.info(`swarmID: \u001b[36m${swarmID}`)
+ let swarmID = null
+ if (!inputs.standalone) {
+ const swarm = await portainer.getSwarm(endpointID)
+ // console.log('swarm:', swarm)
+ swarmID = swarm.ID
+ }
+ core.info(`swarmID: \u001b[36m${swarmID}`)
- // Get Stack
- const stacks = await portainer.getStacks()
- // console.log('stacks:', stacks)
- let stack = stacks.find(
- (item) => item.Name === inputs.name && item.EndpointId === endpointID
- )
- // console.log('stack:', stack)
- let stackID = stack?.Id
- core.info(`stackID: \u001b[36m${stackID}`)
+ // Get Stack
+ const stacks = await portainer.getStacks()
+ // console.log('stacks:', stacks)
+ let stack = stacks.find(
+ (item) => item.Name === inputs.name && item.EndpointId === endpointID
+ )
+ // console.log('stack:', stack)
+ let stackID = stack?.Id
+ core.info(`stackID: \u001b[36m${stackID}`)
- // Update Environment
- const env = getEnv(inputs, stack)
+ // Update Environment
+ const env = getEnv(inputs, stack)
- // Perform Deploy
- if (inputs.type === 'repo') {
- core.info('🌐 Performing Repository Deployment')
- const repositoryAuthentication = !!(inputs.username || inputs.password)
- if (stackID) {
- core.info(`Stack Found - Updating Stack ID: ${stack.Id}`)
- const body = {
- env,
- prune: inputs.prune,
- pullImage: inputs.pull,
- repositoryReferenceName: inputs.ref,
- repositoryAuthentication,
- repositoryPassword: inputs.password,
- repositoryUsername: inputs.username,
- }
- // console.log('body:', body)
- stack = await portainer.updateStackRepo(stackID, endpointID, body)
- // console.log('stack:', stack)
- core.info(`Updated Stack ${stack.Id}: ${stack.Name}`)
- } else {
- core.info('Stack NOT Found - Deploying NEW Stack')
- const body = {
- name: inputs.name,
- swarmID,
- repositoryURL: inputs.repo,
- composeFile: inputs.file,
- env,
- tlsskipVerify: inputs.tlsskip,
- repositoryReferenceName: inputs.ref,
- repositoryAuthentication,
- repositoryPassword: inputs.password,
- repositoryUsername: inputs.username,
- ...(inputs.fs_path && {
- supportRelativePath: true,
- fileSystemPath: inputs.fs_path,
- }),
- }
- // console.log('body:', body)
- stack = await portainer.createStackRepo(endpointID, body)
- // console.log('stack:', stack)
- core.info(`Deployed Stack: ${stack.Id}: ${stack.Name}`)
+ // Perform Deploy
+ if (inputs.type === 'repo') {
+ core.info('🌐 Performing Repository Deployment')
+ const repositoryAuthentication = !!(inputs.username || inputs.password)
+ if (stackID) {
+ core.info(`Stack Found - Updating Stack ID: ${stack.Id}`)
+ const body = {
+ env,
+ prune: inputs.prune,
+ pullImage: inputs.pull,
+ repositoryReferenceName: inputs.ref,
+ repositoryAuthentication,
+ repositoryPassword: inputs.password,
+ repositoryUsername: inputs.username,
}
- } else if (inputs.type === 'file') {
- core.info('📄 Performing Stack File Deployment')
- const stackFileContent = fs.readFileSync(inputs.file, 'utf-8')
- if (stackID) {
- core.info(`Stack Found - Updating Stack ID: ${stackID}`)
- const body = {
- env,
- prune: inputs.prune,
- pullImage: inputs.pull,
- stackFileContent,
- }
- // console.log('body:', body)
- stack = await portainer.updateStackString(stackID, endpointID, body)
- // console.log('stack:', stack)
- core.info(`Updated Stack ${stack.Id}: ${stack.Name}`)
- } else {
- core.info('Stack NOT Found - Deploying NEW Stack')
- const body = {
- name: inputs.name,
- swarmID,
- stackFileContent,
- env,
- }
- // console.log('body:', body)
- stack = await portainer.createStackString(endpointID, body)
- // console.log('stack:', stack)
- core.info(`Deployed Stack: ${stack.Id}: ${stack.Name}`)
+ // console.log('body:', body)
+ stack = await portainer.updateStackRepo(stackID, endpointID, body)
+ // console.log('stack:', stack)
+ core.info(`Updated Stack ${stack.Id}: ${stack.Name}`)
+ } else {
+ core.info('Stack NOT Found - Deploying NEW Stack')
+ const body = {
+ name: inputs.name,
+ swarmID,
+ repositoryURL: inputs.repo,
+ composeFile: inputs.file,
+ env,
+ tlsskipVerify: inputs.tlsskip,
+ repositoryReferenceName: inputs.ref,
+ repositoryAuthentication,
+ repositoryPassword: inputs.password,
+ repositoryUsername: inputs.username,
+ ...(inputs.fs_path && {
+ supportRelativePath: true,
+ fileSystemPath: inputs.fs_path,
+ }),
}
+ // console.log('body:', body)
+ stack = await portainer.createStackRepo(endpointID, body)
+ // console.log('stack:', stack)
+ core.info(`Deployed Stack: ${stack.Id}: ${stack.Name}`)
}
-
- // Set Outputs
- core.info('📩 Setting Outputs')
- core.setOutput('stackID', stack.Id)
- core.setOutput('swarmID', swarmID)
- core.setOutput('endpointID', endpointID)
-
- // Summary
- if (inputs.summary) {
- core.info('📝 Writing Job Summary')
- try {
- await addSummary(inputs, stack)
- } catch (e) {
- console.log(e)
- core.error(`Error writing Job Summary ${e.message}`)
+ } else if (inputs.type === 'file') {
+ core.info('📄 Performing Stack File Deployment')
+ const stackFileContent = fs.readFileSync(inputs.file, 'utf-8')
+ if (stackID) {
+ core.info(`Stack Found - Updating Stack ID: ${stackID}`)
+ const body = {
+ env,
+ prune: inputs.prune,
+ pullImage: inputs.pull,
+ stackFileContent,
}
+ // console.log('body:', body)
+ stack = await portainer.updateStackString(stackID, endpointID, body)
+ // console.log('stack:', stack)
+ core.info(`Updated Stack ${stack.Id}: ${stack.Name}`)
+ } else {
+ core.info('Stack NOT Found - Deploying NEW Stack')
+ const body = {
+ name: inputs.name,
+ swarmID,
+ stackFileContent,
+ env,
+ }
+ // console.log('body:', body)
+ stack = await portainer.createStackString(endpointID, body)
+ // console.log('stack:', stack)
+ core.info(`Deployed Stack: ${stack.Id}: ${stack.Name}`)
}
+ }
- core.info('✅ \u001b[32;1mFinished Success')
- } catch (e) {
- core.debug(e)
- console.log('response:', e.response?.data)
- core.setFailed(e.message)
+ // Set Outputs
+ core.info('📩 Setting Outputs')
+ core.setOutput('stackID', stack.Id)
+ core.setOutput('swarmID', swarmID)
+ core.setOutput('endpointID', endpointID)
+
+ // Summary
+ if (inputs.summary) {
+ core.info('📝 Writing Job Summary')
+ try {
+ await addSummary(inputs, stack)
+ } catch (e) {
+ console.log(e)
+ core.error(`Error writing Job Summary ${e.message}`)
+ }
}
-})()
+
+ core.info('✅ \u001b[32;1mFinished Success')
+}
/**
* @function getEnv
@@ -265,7 +259,7 @@ async function addSummary(inputs, stack) {
}
/**
- * Parse Data from Input
+ * Parse JSON/YAML Data from a String
* @param {string} data
* @return {object}
*/
@@ -337,3 +331,9 @@ function getInputs() {
summary: core.getBooleanInput('summary'),
}
}
+
+main().catch((e) => {
+ core.debug(e)
+ core.info(e.message)
+ core.setFailed(e.message)
+})
From 8fb36780a6a0882b51c2dc18da5188cf9cfa4587 Mon Sep 17 00:00:00 2001
From: Shane <6071159+smashedr@users.noreply.github.com>
Date: Thu, 27 Nov 2025 11:24:37 -0800
Subject: [PATCH 2/2] Updates
---
.github/workflows/check-build.yaml | 2 +-
.github/workflows/labeler.yaml | 2 +-
.github/workflows/lint.yaml | 14 +++---
.github/workflows/mirror.yaml | 2 +-
.github/workflows/pull.yaml | 2 +-
.github/workflows/release.yaml | 1 +
.github/workflows/test.yaml | 3 +-
README.md | 70 ++++++++++++++++--------------
action.yml | 1 +
dist/index.js | 30 ++++++++-----
package-lock.json | 16 +++----
package.json | 4 +-
12 files changed, 81 insertions(+), 66 deletions(-)
diff --git a/.github/workflows/check-build.yaml b/.github/workflows/check-build.yaml
index f835fca..4f47f08 100644
--- a/.github/workflows/check-build.yaml
+++ b/.github/workflows/check-build.yaml
@@ -32,7 +32,7 @@ jobs:
run: echo "$GITHUB_CTX"
- name: "Checkout Pull"
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
with:
ref: ${{ github.event.pull_request.head.sha }}
diff --git a/.github/workflows/labeler.yaml b/.github/workflows/labeler.yaml
index 2f3c44d..6f280f5 100644
--- a/.github/workflows/labeler.yaml
+++ b/.github/workflows/labeler.yaml
@@ -19,7 +19,7 @@ jobs:
steps:
- name: "Checkout Configs"
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
with:
repository: cssnr/configs
ref: master
diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml
index 306a104..2f37923 100644
--- a/.github/workflows/lint.yaml
+++ b/.github/workflows/lint.yaml
@@ -19,10 +19,12 @@ jobs:
permissions:
pull-requests: write
+ statuses: write
+ checks: write
steps:
- name: "Checkout"
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: "Setup Node 24"
uses: actions/setup-node@v6
@@ -38,7 +40,7 @@ jobs:
id: eslint
if: ${{ !cancelled() }}
run: |
- npm run lint:report
+ npm run lint
- name: "Prettier"
if: ${{ !cancelled() }}
@@ -76,7 +78,7 @@ jobs:
run: |
yq -e '.runs.main | test("^dist/")' action.yml
- - name: "ESLint Annotate"
- if: ${{ !cancelled() && steps.eslint.outcome != 'success' }}
- continue-on-error: true
- uses: ataylorme/eslint-annotate-action@d57a1193d4c59cbfbf3f86c271f42612f9dbd9e9 # 3.0.0
+ #- name: "ESLint Annotate"
+ # if: ${{ !cancelled() && steps.eslint.outcome != 'success' }}
+ # continue-on-error: true
+ # uses: ataylorme/eslint-annotate-action@d57a1193d4c59cbfbf3f86c271f42612f9dbd9e9 # 3.0.0
diff --git a/.github/workflows/mirror.yaml b/.github/workflows/mirror.yaml
index db82412..6e90954 100644
--- a/.github/workflows/mirror.yaml
+++ b/.github/workflows/mirror.yaml
@@ -18,7 +18,7 @@ jobs:
steps:
- name: "Checkout"
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
with:
fetch-depth: 0
diff --git a/.github/workflows/pull.yaml b/.github/workflows/pull.yaml
index 3fe1eec..5722a77 100644
--- a/.github/workflows/pull.yaml
+++ b/.github/workflows/pull.yaml
@@ -25,7 +25,7 @@ jobs:
run: echo "$GITHUB_CTX"
- name: "Checkout Pull"
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
with:
ref: ${{ github.event.pull_request.head.sha }}
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index 6177006..26a168c 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -43,3 +43,4 @@ jobs:
uses: sarisia/actions-status-discord@b8381b25576cb341b2af39926ab42c5056cc44ed # v1.15.5
with:
webhook: ${{ secrets.DISCORD_WEBHOOK }}
+ description: ${{ github.event.repository.html_url }}/actions/runs/${{ github.run_id }}
diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index 5ca23e2..c441cf4 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -28,7 +28,7 @@ jobs:
steps:
- name: "Checkout"
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: "Debug CTX github"
if: ${{ !github.event.act }}
@@ -93,3 +93,4 @@ jobs:
uses: sarisia/actions-status-discord@v1
with:
webhook: ${{ secrets.DISCORD_WEBHOOK }}
+ description: ${{ github.event.repository.html_url }}/actions/runs/${{ github.run_id }}
diff --git a/README.md b/README.md
index 5aa6724..9f5eb31 100644
--- a/README.md
+++ b/README.md
@@ -10,9 +10,9 @@
[](https://github.com/cssnr/portainer-stack-deploy-action/pulse)
[](https://codeberg.org/cssnr/portainer-stack-deploy-action)
[](https://portainer-deploy.cssnr.com/)
-[](https://github.com/cssnr/portainer-stack-deploy-action/graphs/contributors)
[](https://github.com/cssnr/portainer-stack-deploy-action?tab=readme-ov-file#readme)
[](https://github.com/cssnr/portainer-stack-deploy-action/blob/master/src)
+[](https://github.com/cssnr/portainer-stack-deploy-action/graphs/contributors)
[](https://github.com/cssnr/portainer-stack-deploy-action/discussions)
[](https://github.com/cssnr/portainer-stack-deploy-action/forks)
[](https://github.com/cssnr/portainer-stack-deploy-action/stargazers)
@@ -22,6 +22,9 @@
# Portainer Stack Deploy Action
+
+
+
- [Features](#Features)
- [Inputs](#Inputs)
- [Outputs](#Outputs)
@@ -31,10 +34,6 @@
- [Support](#Support)
- [Contributing](#Contributing)
-