Initial commit

Seto Elkahfi committed Jun 28, 2026 at 11:48 UTC 6a658e45ce2bba067286542b168f178bc331014d
13 files changed +934
.github/ISSUE_TEMPLATE/bug_report.md new
+40
@@ -0,0 +1,40 @@
1 +---
2 +name: Bug report
3 +about: Create a report to help us improve
4 +title: ''
5 +labels: bug
6 +assignees: ''
7 +
8 +---
9 +
10 +**Describe the bug**
11 +A clear and concise description of what the bug is.
12 +
13 +**Workflow snippet**
14 +A minimal `uses:` step (and any surrounding job config) that reproduces the problem. Redact secrets.
15 +
16 +```yaml
17 +- uses: smbcloudXYZ/smbcloud-deploy-action@v1
18 + with:
19 + token: ${{ secrets.SMB_TOKEN }}
20 + # ...
21 +```
22 +
23 +**To Reproduce**
24 +Steps to reproduce the behavior.
25 +
26 +**Expected behavior**
27 +A clear and concise description of what you expected to happen.
28 +
29 +**Logs**
30 +Relevant job log output (with the action's step expanded). Redact secrets, tokens, and hostnames.
31 +
32 +**Environment**
33 + - Runner OS/arch: [e.g. ubuntu-latest / x86_64]
34 + - Action version (tag or SHA): [e.g. v1.0.0]
35 + - Resolved CLI version: [from the action's `version` output, or `smb --version`]
36 + - smbCloud environment: [production / dev]
37 + - Deploy kind: [nextjs-ssr / vite-spa / rails / rust / swift / rsync / git]
38 +
39 +**Additional context**
40 +Add any other context about the problem here.
.github/ISSUE_TEMPLATE/config.yml new
+8
@@ -0,0 +1,8 @@
1 +blank_issues_enabled: false
2 +contact_links:
3 + - name: Security vulnerability
4 + url: https://github.com/smbcloudXYZ/smbcloud-deploy-action/security/policy
5 + about: Please report security issues privately — do not open a public issue. See SECURITY.md.
6 + - name: smbCloud CLI
7 + url: https://github.com/smbcloudXYZ/smbcloud-cli
8 + about: For issues with the `smb` CLI itself (deploy logic, config), use the CLI repo.
.github/ISSUE_TEMPLATE/feature_request.md new
+20
@@ -0,0 +1,20 @@
1 +---
2 +name: Feature request
3 +about: Suggest an idea for this project
4 +title: ''
5 +labels: enhancement
6 +assignees: ''
7 +
8 +---
9 +
10 +**Is your feature request related to a problem? Please describe.**
11 +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 +
13 +**Describe the solution you'd like**
14 +A clear and concise description of what you want to happen. If it's a new input or output, describe its name, default, and behavior.
15 +
16 +**Describe alternatives you've considered**
17 +A clear and concise description of any alternative solutions or features you've considered.
18 +
19 +**Additional context**
20 +Add any other context, example workflows, or screenshots about the feature request here.
.github/dependabot.yml new
+8
@@ -0,0 +1,8 @@
1 +version: 2
2 +updates:
3 + - package-ecosystem: "github-actions"
4 + directory: "/"
5 + schedule:
6 + interval: "weekly"
7 + labels:
8 + - "dependencies"
.github/pull_request_template.md new
+40
@@ -0,0 +1,40 @@
1 +## Related Issue
2 +
3 +Fixes # <!-- issue number, or remove this line if not applicable -->
4 +
5 +## Description
6 +
7 +<!-- What does this PR do? Why is this change needed? -->
8 +
9 +## Type of Change
10 +
11 +<!-- Mark the relevant option with an [x] -->
12 +
13 +- [ ] Bug fix
14 +- [ ] New feature (new input/output or behavior)
15 +- [ ] Refactor (no functional change)
16 +- [ ] Documentation update
17 +- [ ] CI / tooling change
18 +
19 +## How Has This Been Tested?
20 +
21 +<!-- Describe how you exercised the action -->
22 +
23 +- [ ] `action.yml` parses (YAML)
24 +- [ ] `actionlint` is clean
25 +- [ ] Ran the action in a workflow against a disposable smbCloud project
26 +
27 +## Checklist
28 +
29 +- [ ] Every step is `shell: bash` and starts with `set -euo pipefail`
30 +- [ ] Inputs are passed via `env:` and referenced as `"${INPUT_*}"` (no direct `${{ inputs.* }}` in `run:`)
31 +- [ ] All variable expansions are quoted
32 +- [ ] No secret values are logged
33 +- [ ] No smbCloud fleet detail (hosts, account/user IDs, real domains, project IDs) — placeholders only
34 +- [ ] `README.md` updated if inputs/outputs changed
35 +
36 +## Release Notes
37 +
38 +<!-- One bullet. Use "- Added ...", "- Fixed ...", or "- Improved ..." for user-facing changes. Use "- N/A" otherwise. -->
39 +
40 +-
.github/workflows/lint.yml new
+24
@@ -0,0 +1,24 @@
1 +name: Lint
2 +
3 +on:
4 + push:
5 + branches: [main]
6 + pull_request:
7 +
8 +permissions:
9 + contents: read
10 +
11 +jobs:
12 + actionlint:
13 + name: actionlint
14 + runs-on: ubuntu-latest
15 + steps:
16 + - uses: actions/checkout@v4
17 +
18 + - name: Validate action.yml parses
19 + run: python3 -c "import yaml; yaml.safe_load(open('action.yml'))"
20 +
21 + - name: Run actionlint
22 + uses: docker://rhysd/actionlint:latest
23 + with:
24 + args: -color
.gitignore new
+13
@@ -0,0 +1,13 @@
1 +# OS / editor cruft
2 +.DS_Store
3 +*.swp
4 +.idea/
5 +.vscode/
6 +
7 +# Local secrets / state — never commit
8 +.env
9 +.env.*
10 +.smb/
11 +.smb-dev/
12 +*.pem
13 +*.key
CODE_OF_CONDUCT.md new
+128
@@ -0,0 +1,128 @@
1 +# Contributor Covenant Code of Conduct
2 +
3 +## Our Pledge
4 +
5 +We as members, contributors, and leaders pledge to make participation in our
6 +community a harassment-free experience for everyone, regardless of age, body
7 +size, visible or invisible disability, ethnicity, sex characteristics, gender
8 +identity and expression, level of experience, education, socio-economic status,
9 +nationality, personal appearance, race, religion, or sexual identity
10 +and orientation.
11 +
12 +We pledge to act and interact in ways that contribute to an open, welcoming,
13 +diverse, inclusive, and healthy community.
14 +
15 +## Our Standards
16 +
17 +Examples of behavior that contributes to a positive environment for our
18 +community include:
19 +
20 +* Demonstrating empathy and kindness toward other people
21 +* Being respectful of differing opinions, viewpoints, and experiences
22 +* Giving and gracefully accepting constructive feedback
23 +* Accepting responsibility and apologizing to those affected by our mistakes,
24 + and learning from the experience
25 +* Focusing on what is best not just for us as individuals, but for the
26 + overall community
27 +
28 +Examples of unacceptable behavior include:
29 +
30 +* The use of sexualized language or imagery, and sexual attention or
31 + advances of any kind
32 +* Trolling, insulting or derogatory comments, and personal or political attacks
33 +* Public or private harassment
34 +* Publishing others' private information, such as a physical or email
35 + address, without their explicit permission
36 +* Other conduct which could reasonably be considered inappropriate in a
37 + professional setting
38 +
39 +## Enforcement Responsibilities
40 +
41 +Community leaders are responsible for clarifying and enforcing our standards of
42 +acceptable behavior and will take appropriate and fair corrective action in
43 +response to any behavior that they deem inappropriate, threatening, offensive,
44 +or harmful.
45 +
46 +Community leaders have the right and responsibility to remove, edit, or reject
47 +comments, commits, code, wiki edits, issues, and other contributions that are
48 +not aligned to this Code of Conduct, and will communicate reasons for moderation
49 +decisions when appropriate.
50 +
51 +## Scope
52 +
53 +This Code of Conduct applies within all community spaces, and also applies when
54 +an individual is officially representing the community in public spaces.
55 +Examples of representing our community include using an official e-mail address,
56 +posting via an official social media account, or acting as an appointed
57 +representative at an online or offline event.
58 +
59 +## Enforcement
60 +
61 +Instances of abusive, harassing, or otherwise unacceptable behavior may be
62 +reported to the community leaders responsible for enforcement at
63 +**smbcloud@setoelkahfi.se**.
64 +All complaints will be reviewed and investigated promptly and fairly.
65 +
66 +All community leaders are obligated to respect the privacy and security of the
67 +reporter of any incident.
68 +
69 +## Enforcement Guidelines
70 +
71 +Community leaders will follow these Community Impact Guidelines in determining
72 +the consequences for any action they deem in violation of this Code of Conduct:
73 +
74 +### 1. Correction
75 +
76 +**Community Impact**: Use of inappropriate language or other behavior deemed
77 +unprofessional or unwelcome in the community.
78 +
79 +**Consequence**: A private, written warning from community leaders, providing
80 +clarity around the nature of the violation and an explanation of why the
81 +behavior was inappropriate. A public apology may be requested.
82 +
83 +### 2. Warning
84 +
85 +**Community Impact**: A violation through a single incident or series
86 +of actions.
87 +
88 +**Consequence**: A warning with consequences for continued behavior. No
89 +interaction with the people involved, including unsolicited interaction with
90 +those enforcing the Code of Conduct, for a specified period of time. This
91 +includes avoiding interactions in community spaces as well as external channels
92 +like social media. Violating these terms may lead to a temporary or
93 +permanent ban.
94 +
95 +### 3. Temporary Ban
96 +
97 +**Community Impact**: A serious violation of community standards, including
98 +sustained inappropriate behavior.
99 +
100 +**Consequence**: A temporary ban from any sort of interaction or public
101 +communication with the community for a specified period of time. No public or
102 +private interaction with the people involved, including unsolicited interaction
103 +with those enforcing the Code of Conduct, is allowed during this period.
104 +Violating these terms may lead to a permanent ban.
105 +
106 +### 4. Permanent Ban
107 +
108 +**Community Impact**: Demonstrating a pattern of violation of community
109 +standards, including sustained inappropriate behavior, harassment of an
110 +individual, or aggression toward or disparagement of classes of individuals.
111 +
112 +**Consequence**: A permanent ban from any sort of public interaction within
113 +the community.
114 +
115 +## Attribution
116 +
117 +This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118 +version 2.0, available at
119 +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
120 +
121 +Community Impact Guidelines were inspired by [Mozilla's code of conduct
122 +enforcement ladder](https://github.com/mozilla/diversity).
123 +
124 +[homepage]: https://www.contributor-covenant.org
125 +
126 +For answers to common questions about this code of conduct, see the FAQ at
127 +https://www.contributor-covenant.org/faq. Translations are available at
128 +https://www.contributor-covenant.org/translations.
CONTRIBUTING.md new
+118
@@ -0,0 +1,118 @@
1 +# Contributing to smbcloud-deploy-action
2 +
3 +Thank you for considering contributing! This is a [composite GitHub Action](https://docs.github.com/en/actions/creating-actions/creating-a-composite-action).
4 +The whole thing is `action.yml` plus the bash it runs and the docs around it. There's no build step and no compiler.
5 +
6 +## Code of Conduct
7 +
8 +By participating in this project you agree to abide by our [Code of Conduct](CODE_OF_CONDUCT.md). Please be respectful and considerate of others.
9 +
10 +## Prerequisites
11 +
12 +- A POSIX shell with `bash` (the action's steps are `shell: bash`).
13 +- [`actionlint`](https://github.com/rhysd/actionlint) lints workflow and action YAML.
14 +- [`shellcheck`](https://www.shellcheck.net/) lints the bash inside the steps (actionlint runs it for you when installed).
15 +- [`yq`](https://github.com/mikefarah/yq) or any YAML parser, for a quick syntax check.
16 +
17 +## Getting Started
18 +
19 +1. Fork the repository on GitHub.
20 +2. Clone your fork:
21 + ```sh
22 + git clone https://github.com/your-username/smbcloud-deploy-action.git
23 + cd smbcloud-deploy-action
24 + ```
25 +3. Create a branch for your work:
26 + ```sh
27 + git checkout -b your-feature-or-fix
28 + ```
29 +
30 +## Development Workflow
31 +
32 +### Validate `action.yml`
33 +
34 +Confirm the YAML parses and lint the embedded shell:
35 +
36 +```sh
37 +# YAML parses
38 +python3 -c "import yaml; yaml.safe_load(open('action.yml'))"
39 +
40 +# Lint the action and its embedded bash (runs shellcheck under the hood)
41 +actionlint
42 +```
43 +
44 +### Test it end to end
45 +
46 +The only real test is running the action in a workflow. Add or update an example
47 +workflow under `.github/workflows/` (or test from a throwaway repo) and trigger a
48 +deploy against a disposable smbCloud project. Verify:
49 +
50 +- the correct release binary is downloaded for the runner OS/arch,
51 +- the token lands in the right state dir (`.smb` for production, `.smb-dev` for dev),
52 +- a failed `smb deploy` fails the job (non-zero exit propagates).
53 +
54 +## Coding Guidelines
55 +
56 +These rules are enforced in code review:
57 +
58 +- Keep every step `shell: bash` and start scripts with `set -euo pipefail`.
59 +- Pass inputs into steps via `env:` and reference them as `"${INPUT_NAME}"`. Do
60 + **not** interpolate `${{ inputs.* }}` directly inside a `run:` block, since that
61 + invites shell injection. The action already follows this pattern; keep it.
62 +- Quote all variable expansions.
63 +- Fail fast with `echo "::error::..."` and a non-zero exit on bad input.
64 +- Never log secret values. Token and key contents must not be echoed.
65 +- Keep the action generic: no server hostnames, account/user IDs, real customer
66 + domains, project IDs, or other smbCloud fleet detail. Use placeholders
67 + (`example.com`, `<user-id>`, `<app>`) in docs and examples.
68 +
69 +## Submitting a Pull Request
70 +
71 +1. Ensure the YAML parses and `actionlint` is clean before pushing.
72 +2. Update `README.md` if you add, rename, or change an input or output.
73 +3. Push your branch and open a PR against `main`.
74 +4. A maintainer will review your changes and may request modifications.
75 +
76 +### PR Title Convention
77 +
78 +Use a clear, imperative title that describes the change:
79 +
80 +- ✅ `Add support for arm64 runners`
81 +- ✅ `Fail fast when ssh-key-name is missing`
82 +- ❌ `feat: add arm64` (no conventional-commit prefixes)
83 +- ❌ `Fixed some stuff.` (no past tense, no trailing punctuation)
84 +
85 +## Releasing
86 +
87 +Releases are handled by maintainers via Git tags. Cut a version tag and move the
88 +floating major tag so consumers pinning `@v1` get the update:
89 +
90 +```sh
91 +git tag -a v1.2.0 -m "v1.2.0"
92 +git tag -f v1 v1.2.0
93 +git push origin v1.2.0
94 +git push -f origin v1
95 +```
96 +
97 +## Reporting Bugs
98 +
99 +Use the GitHub issue tracker and fill out the bug report template. Please include:
100 +
101 +- A clear description of the problem.
102 +- A minimal workflow snippet that reproduces it.
103 +- The runner OS, the action version (tag/SHA), and the resolved CLI version.
104 +- Expected vs. actual behaviour.
105 +
106 +## Requesting Features
107 +
108 +Use the GitHub issue tracker and fill out the feature request template. Describe
109 +the use case clearly: what problem does the feature solve?
110 +
111 +## Security
112 +
113 +Please report security vulnerabilities privately. See [SECURITY.md](SECURITY.md).
114 +Do not open a public issue for security reports.
115 +
116 +## Questions?
117 +
118 +Open a GitHub issue or reach out to the maintainers. We're happy to help.
LICENSE new
+190
@@ -0,0 +1,190 @@
1 + Apache License
2 + Version 2.0, January 2004
3 + http://www.apache.org/licenses/
4 +
5 + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 +
7 + 1. Definitions.
8 +
9 + "License" shall mean the terms and conditions for use, reproduction,
10 + and distribution as defined by Sections 1 through 9 of this document.
11 +
12 + "Licensor" shall mean the copyright owner or entity authorized by
13 + the copyright owner that is granting the License.
14 +
15 + "Legal Entity" shall mean the union of the acting entity and all
16 + other entities that control, are controlled by, or are under common
17 + control with that entity. For the purposes of this definition,
18 + "control" means (i) the power, direct or indirect, to cause the
19 + direction or management of such entity, whether by contract or
20 + otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 + outstanding shares, or (iii) beneficial ownership of such entity.
22 +
23 + "You" (or "Your") shall mean an individual or Legal Entity
24 + exercising permissions granted by this License.
25 +
26 + "Source" form shall mean the preferred form for making modifications,
27 + including but not limited to software source code, documentation
28 + source, and configuration files.
29 +
30 + "Object" form shall mean any form resulting from mechanical
31 + transformation or translation of a Source form, including but
32 + not limited to compiled object code, generated documentation,
33 + and conversions to other media types.
34 +
35 + "Work" shall mean the work of authorship made available under
36 + the License, as indicated by a copyright notice that is included in
37 + or attached to the work (an example is provided in the Appendix below).
38 +
39 + "Derivative Works" shall mean any work, whether in Source or Object
40 + form, that is based on (or derived from) the Work and for which the
41 + editorial revisions, annotations, elaborations, or other modifications
42 + represent, as a whole, an original work of authorship. For the purposes
43 + of this License, Derivative Works shall not include works that remain
44 + separable from, or merely link (or bind by name) to the interfaces of,
45 + the Work and Derivative Works thereof.
46 +
47 + "Contribution" shall mean, as defined by the copyright owner or by the
48 + Legal Entity authorized to submit on behalf of the copyright owner, any
49 + work of authorship, including the original version of the Work and any
50 + modifications or additions to that Work or Derivative Works of the Work.
51 +
52 + "Contributor" shall mean Licensor and any Legal Entity on behalf of
53 + whom a Contribution has been received by the Licensor and subsequently
54 + incorporated within the Work.
55 +
56 + 2. Grant of Copyright License. Subject to the terms and conditions of
57 + this License, each Contributor hereby grants to You a perpetual,
58 + worldwide, non-exclusive, no-charge, royalty-free, irrevocable
59 + copyright license to reproduce, prepare Derivative Works of,
60 + publicly display, publicly perform, sublicense, and distribute the
61 + Work and such Derivative Works in Source or Object form.
62 +
63 + 3. Grant of Patent License. Subject to the terms and conditions of
64 + this License, each Contributor hereby grants to You a perpetual,
65 + worldwide, non-exclusive, no-charge, royalty-free, irrevocable
66 + (except as stated in this section) patent license to make, have made,
67 + use, offer to sell, sell, import, and otherwise transfer the Work,
68 + where such license applies only to those patent claims licensable
69 + by such Contributor that are necessarily infringed by their
70 + Contribution(s) alone or by combination of their Contribution(s)
71 + with the Work to which such Contribution(s) was submitted. If You
72 + institute patent litigation against any entity (including a
73 + cross-claim or counterclaim in a lawsuit) alleging that the Work
74 + or a Contribution incorporated within the Work constitutes direct
75 + or contributory patent infringement, then any and all patent licenses
76 + granted to You under this License for that Work shall terminate
77 + as of the date such litigation is filed.
78 +
79 + 4. Redistribution. You may reproduce and distribute copies of the
80 + Work or Derivative Works thereof in any medium, with or without
81 + modifications, and in Source or Object form, provided that You
82 + meet the following conditions:
83 +
84 + (a) You must give any other recipients of the Work or
85 + Derivative Works a copy of this License; and
86 +
87 + (b) You must cause any modified files to carry prominent notices
88 + stating that You changed the files; and
89 +
90 + (c) You must retain, in the Source form of any Derivative Works
91 + that You distribute, all copyright, patent, trademark, and
92 + attribution notices from the Source form of the Work,
93 + excluding those notices that do not pertain to any part of
94 + the Derivative Works; and
95 +
96 + (d) If the Work includes a "NOTICE" text file as part of its
97 + distribution, You must include a readable copy of the
98 + attribution notices contained within such NOTICE file, in
99 + at least one of the following places: within a NOTICE text
100 + file distributed as part of the Derivative Works; within
101 + the Source form or documentation, if provided along with the
102 + Derivative Works; or, within a display generated by the
103 + Derivative Works, if and wherever such third-party notices
104 + normally appear. The contents of the NOTICE file are for
105 + informational purposes only and do not modify the License.
106 + You may add Your own attribution notices within Derivative
107 + Works that You distribute, alongside or as an addendum to
108 + the NOTICE text from the Work, provided that such additional
109 + attribution notices cannot be construed as modifying the License.
110 +
111 + You may add Your own license statement for Your modifications and
112 + may provide additional grant of rights to use, copy, modify, merge,
113 + publish, distribute, sublicense, and/or sell copies of the Work,
114 + and to permit persons to whom the Work is furnished to do so,
115 + subject to the following conditions:
116 +
117 + The above copyright notice and this permission notice shall be
118 + included in all copies or substantial portions of the Work.
119 +
120 + 5. Submission of Contributions. Unless You explicitly state otherwise,
121 + any Contribution intentionally submitted for inclusion in the Work
122 + by You to the Licensor shall be under the terms and conditions of
123 + this License, without any additional terms or conditions.
124 + Notwithstanding the above, nothing herein shall supersede or modify
125 + the terms of any separate license agreement you may have executed
126 + with Licensor regarding such Contributions.
127 +
128 + 6. Trademarks. This License does not grant permission to use the trade
129 + names, trademarks, service marks, or product names of the Licensor,
130 + except as required for reasonable and customary use in describing the
131 + origin of the Work and reproducing the content of the NOTICE file.
132 +
133 + 7. Disclaimer of Warranty. Unless required by applicable law or
134 + agreed to in writing, Licensor provides the Work (and each
135 + Contributor provides its Contributions) on an "AS IS" BASIS,
136 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
137 + implied, including, without limitation, any warranties or conditions
138 + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
139 + PARTICULAR PURPOSE. You are solely responsible for determining the
140 + appropriateness of using or redistributing the Work and assume any
141 + risks associated with Your exercise of permissions under this License.
142 +
143 + 8. Limitation of Liability. In no event and under no legal theory,
144 + whether in tort (including negligence), contract, or otherwise,
145 + unless required by applicable law (such as deliberate and grossly
146 + negligent acts) or agreed to in writing, shall any Contributor be
147 + liable to You for damages, including any direct, indirect, special,
148 + incidental, or exemplary damages of any character arising as a
149 + result of this License or out of the use or inability to use the
150 + Work (including but not limited to damages for loss of goodwill,
151 + work stoppage, computer failure or malfunction, or all other
152 + commercial damages or losses), even if such Contributor has been
153 + advised of the possibility of such damages.
154 +
155 + 9. Accepting Warranty or Additional Liability. While redistributing
156 + the Work or Derivative Works thereof, You may choose to offer,
157 + and charge a fee for, acceptance of support, warranty, indemnity,
158 + or other liability obligations and/or rights consistent with this
159 + License. However, in accepting such obligations, You may act only
160 + on Your own behalf and on Your sole responsibility, not on behalf
161 + of any other Contributor, and only if You agree to indemnify,
162 + defend, and hold each Contributor harmless for any liability
163 + incurred by, or claims asserted against, such Contributor by reason
164 + of your accepting any such warranty or additional liability.
165 +
166 + END OF TERMS AND CONDITIONS
167 +
168 + APPENDIX: How to apply the Apache License to your work.
169 +
170 + To apply the Apache License to your work, attach the following
171 + boilerplate notice, with the fields enclosed by brackets "[]"
172 + replaced with your own identifying information. (Don't include
173 + the brackets!) The text should be enclosed in the appropriate
174 + comment syntax for the file format in use. Also, it is recommended
175 + that a file or directory name be given on the same line as the
176 + copyright notice for easier identification within third-party archives.
177 +
178 + Copyright 2026 smbCloud (Splitfire AB)
179 +
180 + Licensed under the Apache License, Version 2.0 (the "License");
181 + you may not use this file except in compliance with the License.
182 + You may obtain a copy of the License at
183 +
184 + http://www.apache.org/licenses/LICENSE-2.0
185 +
186 + Unless required by applicable law or agreed to in writing, software
187 + distributed under the License is distributed on an "AS IS" BASIS,
188 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
189 + See the License for the specific language governing permissions and
190 + limitations under the License.
README.md new
+105
@@ -0,0 +1,105 @@
1 +# smbCloud Deploy Action
2 +
3 +A composite GitHub Action that installs the [smbCloud CLI](https://github.com/smbcloudXYZ/smbcloud-cli)
4 +(`smb`) and deploys your app non-interactively from CI.
5 +
6 +It wraps the CLI's `--ci` (non-interactive) deploy flow: provision a token,
7 +optionally install an SSH key, then run `smb --ci deploy`. Your `.smb/config.toml`
8 +picks the deploy strategy (`nextjs-ssr`, `vite-spa`, `rails`, `rust`, `swift`, or
9 +generic rsync/git), so the same step works for any stack.
10 +
11 +## Quick start
12 +
13 +```yaml
14 +name: Deploy
15 +on:
16 + push:
17 + branches: [main]
18 +
19 +jobs:
20 + deploy:
21 + runs-on: ubuntu-latest
22 + steps:
23 + - uses: actions/checkout@v4
24 + - uses: smbcloudXYZ/smbcloud-deploy-action@v1
25 + with:
26 + token: ${{ secrets.SMB_TOKEN }}
27 +```
28 +
29 +That installs the latest CLI, writes the token, and runs `smb --ci deploy`
30 +against the project pinned in your `.smb/config.toml`.
31 +
32 +## Monorepo
33 +
34 +For a monorepo config with several `[[projects]]` entries, name the target.
35 +Non-interactive mode won't prompt you to choose:
36 +
37 +```yaml
38 + - uses: smbcloudXYZ/smbcloud-deploy-action@v1
39 + with:
40 + token: ${{ secrets.SMB_TOKEN }}
41 + project: my-web-app
42 +```
43 +
44 +## SSH / rsync deploys
45 +
46 +Deploy paths that upload over rsync need the account-scoped SSH key. smbCloud
47 +expects it at `~/.ssh/id_<user-id>@smbcloud`:
48 +
49 +```yaml
50 + - uses: smbcloudXYZ/smbcloud-deploy-action@v1
51 + with:
52 + token: ${{ secrets.SMB_TOKEN }}
53 + ssh-private-key: ${{ secrets.SMB_SSH_KEY }}
54 + ssh-key-name: id_<user-id>@smbcloud
55 + ssh-known-hosts: ${{ secrets.SMB_KNOWN_HOSTS }}
56 +```
57 +
58 +Pin `ssh-known-hosts` instead of disabling host-key checking.
59 +
60 +## Inputs
61 +
62 +| Input | Required | Default | Description |
63 +| ------------------- | -------- | ------------------------ | ---------------------------------------------------------------------------------------------------- |
64 +| `token` | yes | _(required)_ | smbCloud auth token contents (from a CI secret). Written to the CLI state dir to authenticate `smb`. |
65 +| `environment` | no | `production` | `production` or `dev`. Selects API host and state dir (`.smb` vs `.smb-dev`). |
66 +| `project` | no | `""` | Monorepo sub-project name (the `name` in a `[[projects]]` entry). |
67 +| `working-directory` | no | `.` | Directory containing `.smb/config.toml`. |
68 +| `version` | no | `latest` | CLI version to install (e.g. `0.5.0`, `v0.5.0`, or `latest`). |
69 +| `ssh-private-key` | no | `""` | SSH private key contents for rsync/SSH deploys. |
70 +| `ssh-key-name` | no | `""` | Filename under `~/.ssh` for the key. Required when `ssh-private-key` is set. |
71 +| `ssh-known-hosts` | no | `""` | Entries appended to `~/.ssh/known_hosts`. |
72 +| `args` | no | `""` | Extra arguments appended to `smb deploy`. |
73 +| `cli-repository` | no | `smbcloudXYZ/smbcloud-cli` | Repository to download the CLI binary from. |
74 +
75 +## Outputs
76 +
77 +| Output | Description |
78 +| ---------- | ---------------------------------------- |
79 +| `smb-path` | Absolute path to the installed `smb` binary. |
80 +| `version` | Resolved CLI version that was installed. |
81 +
82 +After this step runs, `smb` is on `PATH` and authenticated, so later steps can
83 +call it directly (e.g. `smb me`, `smb migrate`).
84 +
85 +## How it works
86 +
87 +1. **Install.** Downloads the prebuilt `smb-<os>-<arch>` release binary for the
88 + runner, makes it executable, and adds it to `PATH`. Works on Linux, macOS, and
89 + Windows runners (amd64/arm64).
90 +2. **Configure.** Writes the token to `~/.smb/token` (production) or
91 + `~/.smb-dev/token` (dev) with `600` perms, then installs the SSH key and
92 + `known_hosts` if you passed them.
93 +3. **Deploy.** Runs `smb --ci --environment <env> deploy [--project <name>]` in
94 + `working-directory`. The exit code propagates, so a failed deploy fails the job.
95 +
96 +## Provisioning the token
97 +
98 +Logging in is interactive and cannot run under `--ci`, so provision the token
99 +ahead of time and store its contents in a CI secret. See
100 +[`docs/ci.md`](https://github.com/smbcloudXYZ/smbcloud-cli/blob/main/docs/ci.md)
101 +in the CLI repo for token setup and the full non-interactive behavior reference.
102 +
103 +## License
104 +
105 +[Apache-2.0](LICENSE)
SECURITY.md new
+42
@@ -0,0 +1,42 @@
1 +# Security Policy
2 +
3 +The smbCloud team takes security vulnerabilities seriously. We appreciate your efforts to responsibly disclose your findings, and we will make every effort to acknowledge your contributions.
4 +
5 +## Supported Versions
6 +
7 +This action is versioned by Git tag. Security patches are provided for the latest major version tag (e.g. `v1`). Please pin to a major version tag (or a specific release) and keep it up to date.
8 +
9 +| Version | Supported |
10 +| ------- | ------------------ |
11 +| `v1` | :white_check_mark: |
12 +| `< v1` | :x: |
13 +
14 +## Reporting a Vulnerability
15 +
16 +If you discover a security vulnerability, please report it to us by emailing **smbcloud@setoelkahfi.se**.
17 +
18 +**Please do not report security vulnerabilities through public GitHub issues.**
19 +
20 +### What to Include in a Vulnerability Report
21 +
22 +Please include the following details in your report:
23 +
24 +- A clear and concise description of the vulnerability.
25 +- The version (tag or commit SHA) of the action affected.
26 +- Steps to reproduce the vulnerability. This could include a workflow snippet or a link to a repository with a proof of concept.
27 +- The potential impact of the vulnerability.
28 +- Any potential mitigations or workarounds you are aware of.
29 +
30 +### Our Process
31 +
32 +1. When you report a vulnerability, we will acknowledge receipt of your report within 48 hours.
33 +2. We will investigate the report and determine if it is a valid security issue.
34 +3. If the vulnerability is accepted, we will work on a patch for the latest version as soon as possible.
35 +4. We will notify you when the patch is released.
36 +5. We will publicly disclose the vulnerability after the patch has been released, and we will credit you for the discovery unless you prefer to remain anonymous.
37 +
38 +## Handling Secrets
39 +
40 +This action reads sensitive inputs (`token`, `ssh-private-key`). Always pass these from [GitHub Actions secrets](https://docs.github.com/en/actions/security-guides/encrypted-secrets). Never hardcode them in a workflow file. The action writes them to the runner filesystem with `600` permissions and leaves cleanup to the ephemeral runner. Use a dedicated smbCloud token for CI with only the access it needs.
41 +
42 +We value your contributions to keeping smbCloud secure.
action.yml new
+198
@@ -0,0 +1,198 @@
1 +name: "smbCloud Deploy"
2 +description: "Install the smbCloud CLI (smb) and deploy an app non-interactively from CI."
3 +author: "smbCloud (Splitfire AB)"
4 +
5 +branding:
6 + icon: "upload-cloud"
7 + color: "blue"
8 +
9 +inputs:
10 + token:
11 + description: >-
12 + smbCloud auth token contents. Provision it once (see docs/ci.md in
13 + smbcloud-cli) and pass it from a CI secret. Written to the CLI state dir
14 + so `smb` is authenticated without an interactive `smb login`.
15 + required: true
16 + environment:
17 + description: "smbCloud environment: production or dev."
18 + required: false
19 + default: "production"
20 + project:
21 + description: >-
22 + Sub-project name to deploy for monorepo configs with [[projects]] entries.
23 + Matches the `name` field in .smb/config.toml. Omit for single-project configs.
24 + required: false
25 + default: ""
26 + working-directory:
27 + description: "Directory containing .smb/config.toml. Defaults to the repo root."
28 + required: false
29 + default: "."
30 + version:
31 + description: >-
32 + smbCloud CLI version to install (e.g. 0.5.0 or v0.5.0). Use 'latest' for
33 + the most recent GitHub release.
34 + required: false
35 + default: "latest"
36 + ssh-private-key:
37 + description: >-
38 + Optional SSH private key contents for rsync/SSH deploy paths. Installed at
39 + ~/.ssh/<ssh-key-name>.
40 + required: false
41 + default: ""
42 + ssh-key-name:
43 + description: >-
44 + Filename for the installed SSH private key under ~/.ssh. smbCloud rsync
45 + deploys expect the account-scoped name `id_<user-id>@smbcloud`. Required
46 + when ssh-private-key is set.
47 + required: false
48 + default: ""
49 + ssh-known-hosts:
50 + description: >-
51 + Optional known_hosts entries appended to ~/.ssh/known_hosts. Recommended
52 + for SSH deploys instead of disabling host-key checking.
53 + required: false
54 + default: ""
55 + args:
56 + description: "Extra arguments appended to the `smb deploy` invocation."
57 + required: false
58 + default: ""
59 + cli-repository:
60 + description: "GitHub repository to download the CLI binary from."
61 + required: false
62 + default: "smbcloudXYZ/smbcloud-cli"
63 +
64 +outputs:
65 + smb-path:
66 + description: "Absolute path to the installed smb binary."
67 + value: ${{ steps.install.outputs.smb-path }}
68 + version:
69 + description: "Resolved CLI version that was installed."
70 + value: ${{ steps.install.outputs.version }}
71 +
72 +runs:
73 + using: "composite"
74 + steps:
75 + - name: Install smbCloud CLI
76 + id: install
77 + shell: bash
78 + env:
79 + INPUT_VERSION: ${{ inputs.version }}
80 + INPUT_CLI_REPOSITORY: ${{ inputs.cli-repository }}
81 + run: |
82 + set -euo pipefail
83 +
84 + case "$(uname -s)" in
85 + Linux) os="linux" ;;
86 + Darwin) os="macos" ;;
87 + MINGW*|MSYS*|CYGWIN*) os="win" ;;
88 + *) echo "::error::Unsupported OS: $(uname -s)"; exit 1 ;;
89 + esac
90 +
91 + case "$(uname -m)" in
92 + x86_64|amd64) arch="amd64" ;;
93 + arm64|aarch64) arch="arm64" ;;
94 + *) echo "::error::Unsupported architecture: $(uname -m)"; exit 1 ;;
95 + esac
96 +
97 + asset="smb-${os}-${arch}"
98 + bin_name="smb"
99 + if [ "$os" = "win" ]; then
100 + asset="${asset}.exe"
101 + bin_name="smb.exe"
102 + fi
103 +
104 + if [ "${INPUT_VERSION}" = "latest" ]; then
105 + url="https://github.com/${INPUT_CLI_REPOSITORY}/releases/latest/download/${asset}"
106 + else
107 + tag="${INPUT_VERSION#v}"
108 + url="https://github.com/${INPUT_CLI_REPOSITORY}/releases/download/v${tag}/${asset}"
109 + fi
110 +
111 + install_dir="${RUNNER_TEMP:-/tmp}/smbcloud-cli-bin"
112 + mkdir -p "$install_dir"
113 + smb_path="${install_dir}/${bin_name}"
114 +
115 + echo "Downloading ${asset} from ${url}"
116 + curl --fail --location --retry 3 --retry-delay 2 --proto '=https' --tlsv1.2 \
117 + --output "$smb_path" "$url"
118 + chmod +x "$smb_path"
119 +
120 + echo "$install_dir" >> "$GITHUB_PATH"
121 + echo "smb-path=${smb_path}" >> "$GITHUB_OUTPUT"
122 +
123 + resolved_version="$("$smb_path" --version 2>/dev/null | awk '{print $NF}' || echo "${INPUT_VERSION}")"
124 + echo "version=${resolved_version}" >> "$GITHUB_OUTPUT"
125 + echo "Installed smb ${resolved_version} at ${smb_path}"
126 +
127 + - name: Configure smbCloud credentials
128 + shell: bash
129 + env:
130 + INPUT_TOKEN: ${{ inputs.token }}
131 + INPUT_ENVIRONMENT: ${{ inputs.environment }}
132 + INPUT_SSH_PRIVATE_KEY: ${{ inputs.ssh-private-key }}
133 + INPUT_SSH_KEY_NAME: ${{ inputs.ssh-key-name }}
134 + INPUT_SSH_KNOWN_HOSTS: ${{ inputs.ssh-known-hosts }}
135 + run: |
136 + set -euo pipefail
137 +
138 + if [ -z "${INPUT_TOKEN}" ]; then
139 + echo "::error::'token' input is required."
140 + exit 1
141 + fi
142 +
143 + case "${INPUT_ENVIRONMENT}" in
144 + production) smb_dir=".smb" ;;
145 + dev) smb_dir=".smb-dev" ;;
146 + *) echo "::error::Invalid environment '${INPUT_ENVIRONMENT}' (use 'production' or 'dev')."; exit 1 ;;
147 + esac
148 +
149 + state_dir="${HOME}/${smb_dir}"
150 + mkdir -p "$state_dir"
151 + umask 077
152 + printf '%s' "${INPUT_TOKEN}" > "${state_dir}/token"
153 + chmod 600 "${state_dir}/token"
154 + echo "Wrote auth token to ${state_dir}/token"
155 +
156 + if [ -n "${INPUT_SSH_PRIVATE_KEY}" ]; then
157 + if [ -z "${INPUT_SSH_KEY_NAME}" ]; then
158 + echo "::error::'ssh-key-name' is required when 'ssh-private-key' is set."
159 + exit 1
160 + fi
161 + ssh_dir="${HOME}/.ssh"
162 + mkdir -p "$ssh_dir"
163 + chmod 700 "$ssh_dir"
164 + key_path="${ssh_dir}/${INPUT_SSH_KEY_NAME}"
165 + printf '%s\n' "${INPUT_SSH_PRIVATE_KEY}" > "$key_path"
166 + chmod 600 "$key_path"
167 + echo "Installed SSH key at ${key_path}"
168 +
169 + if [ -n "${INPUT_SSH_KNOWN_HOSTS}" ]; then
170 + printf '%s\n' "${INPUT_SSH_KNOWN_HOSTS}" >> "${ssh_dir}/known_hosts"
171 + chmod 600 "${ssh_dir}/known_hosts"
172 + echo "Appended entries to ${ssh_dir}/known_hosts"
173 + fi
174 + fi
175 +
176 + - name: Deploy
177 + shell: bash
178 + working-directory: ${{ inputs.working-directory }}
179 + env:
180 + SMB_CI: "1"
181 + INPUT_ENVIRONMENT: ${{ inputs.environment }}
182 + INPUT_PROJECT: ${{ inputs.project }}
183 + INPUT_ARGS: ${{ inputs.args }}
184 + run: |
185 + set -euo pipefail
186 +
187 + cmd=(smb --ci --environment "${INPUT_ENVIRONMENT}" deploy)
188 + if [ -n "${INPUT_PROJECT}" ]; then
189 + cmd+=(--project "${INPUT_PROJECT}")
190 + fi
191 + if [ -n "${INPUT_ARGS}" ]; then
192 + # shellcheck disable=SC2206
193 + extra=(${INPUT_ARGS})
194 + cmd+=("${extra[@]}")
195 + fi
196 +
197 + echo "Running: ${cmd[*]}"
198 + "${cmd[@]}"