Compare commits
2 Commits
v1
...
lucy/updat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea085fcce0 | ||
|
|
85386695a5 |
9
.github/workflows/lint.yml
vendored
9
.github/workflows/lint.yml
vendored
@@ -5,9 +5,6 @@ jobs:
|
|||||||
lint:
|
lint:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v2
|
||||||
- name: Run ShellCheck
|
- name: ShellCheck
|
||||||
uses: ludeeus/action-shellcheck@2.0.0
|
uses: ludeeus/action-shellcheck@1.1.0
|
||||||
with:
|
|
||||||
ignore_paths: >-
|
|
||||||
.husky
|
|
||||||
|
|||||||
138
.github/workflows/test.yml
vendored
138
.github/workflows/test.yml
vendored
@@ -2,40 +2,24 @@ on: push
|
|||||||
name: Run acceptance tests
|
name: Run acceptance tests
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test-with-output-secrets:
|
use-connect-without-export-env:
|
||||||
strategy:
|
runs-on: ubuntu-latest
|
||||||
matrix:
|
|
||||||
os: [ubuntu-latest, macos-latest]
|
|
||||||
auth: [connect, service-account]
|
|
||||||
exclude:
|
|
||||||
- os: macos-latest
|
|
||||||
auth: connect
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v2
|
||||||
- name: Launch 1Password Connect instance
|
- name: Launch 1Password Connect instance
|
||||||
if: ${{ matrix.auth == 'connect' }}
|
|
||||||
env:
|
env:
|
||||||
OP_CONNECT_CREDENTIALS: ${{ secrets.OP_CONNECT_CREDENTIALS }}
|
OP_CONNECT_CREDENTIALS: ${{ secrets.OP_CONNECT_CREDENTIALS }}
|
||||||
run: |
|
run: |
|
||||||
echo "$OP_CONNECT_CREDENTIALS" > 1password-credentials.json
|
echo "$OP_CONNECT_CREDENTIALS" > 1password-credentials.json
|
||||||
docker-compose -f tests/fixtures/docker-compose.yml up -d && sleep 10
|
docker-compose -f tests/fixtures/docker-compose.yml up -d && sleep 10
|
||||||
- name: Configure Service account
|
|
||||||
if: ${{ matrix.auth == 'service-account' }}
|
|
||||||
uses: ./configure
|
|
||||||
with:
|
|
||||||
service-account-token: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
|
|
||||||
- name: Configure 1Password Connect
|
- name: Configure 1Password Connect
|
||||||
if: ${{ matrix.auth == 'connect' }}
|
|
||||||
uses: ./configure # 1password/load-secrets-action/configure@<version>
|
uses: ./configure # 1password/load-secrets-action/configure@<version>
|
||||||
with:
|
with:
|
||||||
connect-host: localhost:8080
|
connect-host: http://localhost:8080
|
||||||
connect-token: ${{ secrets.OP_CONNECT_TOKEN }}
|
connect-token: ${{ secrets.OP_CONNECT_TOKEN }}
|
||||||
- name: Load secrets
|
- name: Load secrets
|
||||||
id: load_secrets
|
id: load_secrets
|
||||||
uses: ./ # 1password/load-secrets-action@<version>
|
uses: ./ # 1password/load-secrets-action@<version>
|
||||||
with:
|
|
||||||
export-env: false
|
|
||||||
env:
|
env:
|
||||||
SECRET: op://acceptance-tests/test-secret/password
|
SECRET: op://acceptance-tests/test-secret/password
|
||||||
SECRET_IN_SECTION: op://acceptance-tests/test-secret/test-section/password
|
SECRET_IN_SECTION: op://acceptance-tests/test-secret/test-section/password
|
||||||
@@ -46,31 +30,17 @@ jobs:
|
|||||||
SECRET_IN_SECTION: ${{ steps.load_secrets.outputs.SECRET_IN_SECTION }}
|
SECRET_IN_SECTION: ${{ steps.load_secrets.outputs.SECRET_IN_SECTION }}
|
||||||
MULTILINE_SECRET: ${{ steps.load_secrets.outputs.MULTILINE_SECRET }}
|
MULTILINE_SECRET: ${{ steps.load_secrets.outputs.MULTILINE_SECRET }}
|
||||||
run: ./tests/assert-env-set.sh
|
run: ./tests/assert-env-set.sh
|
||||||
test-with-export-env:
|
use-connect-with-export-env:
|
||||||
strategy:
|
runs-on: ubuntu-latest
|
||||||
matrix:
|
|
||||||
os: [ubuntu-latest, macos-latest]
|
|
||||||
auth: [connect, service-account]
|
|
||||||
exclude:
|
|
||||||
- os: macos-latest
|
|
||||||
auth: connect
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v2
|
||||||
- name: Launch 1Password Connect instance
|
- name: Launch 1Password Connect instance
|
||||||
if: ${{ matrix.auth == 'connect' }}
|
|
||||||
env:
|
env:
|
||||||
OP_CONNECT_CREDENTIALS: ${{ secrets.OP_CONNECT_CREDENTIALS }}
|
OP_CONNECT_CREDENTIALS: ${{ secrets.OP_CONNECT_CREDENTIALS }}
|
||||||
run: |
|
run: |
|
||||||
echo "$OP_CONNECT_CREDENTIALS" > 1password-credentials.json
|
echo "$OP_CONNECT_CREDENTIALS" > 1password-credentials.json
|
||||||
docker-compose -f tests/fixtures/docker-compose.yml up -d && sleep 10
|
docker-compose -f tests/fixtures/docker-compose.yml up -d && sleep 10
|
||||||
- name: Configure Service account
|
|
||||||
if: ${{ matrix.auth == 'service-account' }}
|
|
||||||
uses: ./configure
|
|
||||||
with:
|
|
||||||
service-account-token: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
|
|
||||||
- name: Configure 1Password Connect
|
- name: Configure 1Password Connect
|
||||||
if: ${{ matrix.auth == 'connect' }}
|
|
||||||
uses: ./configure # 1password/load-secrets-action/configure@<version>
|
uses: ./configure # 1password/load-secrets-action/configure@<version>
|
||||||
with:
|
with:
|
||||||
connect-host: http://localhost:8080
|
connect-host: http://localhost:8080
|
||||||
@@ -78,6 +48,8 @@ jobs:
|
|||||||
- name: Load secrets
|
- name: Load secrets
|
||||||
id: load_secrets
|
id: load_secrets
|
||||||
uses: ./ # 1password/load-secrets-action@<version>
|
uses: ./ # 1password/load-secrets-action@<version>
|
||||||
|
with:
|
||||||
|
export-env: true
|
||||||
env:
|
env:
|
||||||
SECRET: op://acceptance-tests/test-secret/password
|
SECRET: op://acceptance-tests/test-secret/password
|
||||||
SECRET_IN_SECTION: op://acceptance-tests/test-secret/test-section/password
|
SECRET_IN_SECTION: op://acceptance-tests/test-secret/test-section/password
|
||||||
@@ -90,31 +62,17 @@ jobs:
|
|||||||
unset-previous: true
|
unset-previous: true
|
||||||
- name: Assert removed secrets
|
- name: Assert removed secrets
|
||||||
run: ./tests/assert-env-unset.sh
|
run: ./tests/assert-env-unset.sh
|
||||||
test-references-with-ids:
|
use-connect-with-references-with-id:
|
||||||
strategy:
|
runs-on: ubuntu-latest
|
||||||
matrix:
|
|
||||||
os: [ubuntu-latest, macos-latest]
|
|
||||||
auth: [connect, service-account]
|
|
||||||
exclude:
|
|
||||||
- os: macos-latest
|
|
||||||
auth: connect
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v2
|
||||||
- name: Launch 1Password Connect instance
|
- name: Launch 1Password Connect instance
|
||||||
if: ${{ matrix.auth == 'connect' }}
|
|
||||||
env:
|
env:
|
||||||
OP_CONNECT_CREDENTIALS: ${{ secrets.OP_CONNECT_CREDENTIALS }}
|
OP_CONNECT_CREDENTIALS: ${{ secrets.OP_CONNECT_CREDENTIALS }}
|
||||||
run: |
|
run: |
|
||||||
echo "$OP_CONNECT_CREDENTIALS" > 1password-credentials.json
|
echo "$OP_CONNECT_CREDENTIALS" > 1password-credentials.json
|
||||||
docker-compose -f tests/fixtures/docker-compose.yml up -d && sleep 10
|
docker-compose -f tests/fixtures/docker-compose.yml up -d && sleep 10
|
||||||
- name: Configure Service account
|
|
||||||
if: ${{ matrix.auth == 'service-account' }}
|
|
||||||
uses: ./configure
|
|
||||||
with:
|
|
||||||
service-account-token: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
|
|
||||||
- name: Configure 1Password Connect
|
- name: Configure 1Password Connect
|
||||||
if: ${{ matrix.auth == 'connect' }}
|
|
||||||
uses: ./configure # 1password/load-secrets-action/configure@<version>
|
uses: ./configure # 1password/load-secrets-action/configure@<version>
|
||||||
with:
|
with:
|
||||||
connect-host: http://localhost:8080
|
connect-host: http://localhost:8080
|
||||||
@@ -122,8 +80,6 @@ jobs:
|
|||||||
- name: Load secrets
|
- name: Load secrets
|
||||||
id: load_secrets
|
id: load_secrets
|
||||||
uses: ./ # 1password/load-secrets-action@<version>
|
uses: ./ # 1password/load-secrets-action@<version>
|
||||||
with:
|
|
||||||
export-env: false
|
|
||||||
env:
|
env:
|
||||||
SECRET: op://v5pz6venw4roosmkzdq2nhpv6u/hrgkzhrlvscomepxlgafb2m3ca/password
|
SECRET: op://v5pz6venw4roosmkzdq2nhpv6u/hrgkzhrlvscomepxlgafb2m3ca/password
|
||||||
SECRET_IN_SECTION: op://v5pz6venw4roosmkzdq2nhpv6u/hrgkzhrlvscomepxlgafb2m3ca/Section_tco6nsqycj6jcbyx63h5isxcny/doxu3mhkozcznnk5vjrkpdqayy
|
SECRET_IN_SECTION: op://v5pz6venw4roosmkzdq2nhpv6u/hrgkzhrlvscomepxlgafb2m3ca/Section_tco6nsqycj6jcbyx63h5isxcny/doxu3mhkozcznnk5vjrkpdqayy
|
||||||
@@ -134,3 +90,73 @@ jobs:
|
|||||||
SECRET_IN_SECTION: ${{ steps.load_secrets.outputs.SECRET_IN_SECTION }}
|
SECRET_IN_SECTION: ${{ steps.load_secrets.outputs.SECRET_IN_SECTION }}
|
||||||
MULTILINE_SECRET: ${{ steps.load_secrets.outputs.MULTILINE_SECRET }}
|
MULTILINE_SECRET: ${{ steps.load_secrets.outputs.MULTILINE_SECRET }}
|
||||||
run: ./tests/assert-env-set.sh
|
run: ./tests/assert-env-set.sh
|
||||||
|
use-service-account-without-export-env:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Load secrets
|
||||||
|
id: load_secrets
|
||||||
|
uses: ./ # 1password/load-secrets-action@<version>
|
||||||
|
env:
|
||||||
|
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
|
||||||
|
SECRET: op://acceptance-tests/test-secret/password
|
||||||
|
SECRET_IN_SECTION: op://acceptance-tests/test-secret/test-section/password
|
||||||
|
MULTILINE_SECRET: op://acceptance-tests/multiline-secret/notesPlain
|
||||||
|
- name: Assert test secret values
|
||||||
|
env:
|
||||||
|
SECRET: ${{ steps.load_secrets.outputs.SECRET }}
|
||||||
|
SECRET_IN_SECTION: ${{ steps.load_secrets.outputs.SECRET_IN_SECTION }}
|
||||||
|
MULTILINE_SECRET: ${{ steps.load_secrets.outputs.MULTILINE_SECRET }}
|
||||||
|
run: ./tests/assert-env-set.sh
|
||||||
|
use-service-account-with-export-env:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Load secrets
|
||||||
|
id: load_secrets
|
||||||
|
uses: ./ # 1password/load-secrets-action@<version>
|
||||||
|
with:
|
||||||
|
export-env: true
|
||||||
|
env:
|
||||||
|
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
|
||||||
|
SECRET: op://acceptance-tests/test-secret/password
|
||||||
|
SECRET_IN_SECTION: op://acceptance-tests/test-secret/test-section/password
|
||||||
|
MULTILINE_SECRET: op://acceptance-tests/multiline-secret/notesPlain
|
||||||
|
- name: Assert test secret values
|
||||||
|
run: ./tests/assert-env-set.sh
|
||||||
|
use-service-account-with-references-with-id:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Load secrets
|
||||||
|
id: load_secrets
|
||||||
|
uses: ./ # 1password/load-secrets-action@<version>
|
||||||
|
env:
|
||||||
|
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
|
||||||
|
SECRET: op://v5pz6venw4roosmkzdq2nhpv6u/hrgkzhrlvscomepxlgafb2m3ca/password
|
||||||
|
SECRET_IN_SECTION: op://v5pz6venw4roosmkzdq2nhpv6u/hrgkzhrlvscomepxlgafb2m3ca/Section_tco6nsqycj6jcbyx63h5isxcny/doxu3mhkozcznnk5vjrkpdqayy
|
||||||
|
MULTILINE_SECRET: op://v5pz6venw4roosmkzdq2nhpv6u/ghtz3jvcc6dqmzc53d3r3eskge/notesPlain
|
||||||
|
- name: Assert test secret values
|
||||||
|
env:
|
||||||
|
SECRET: ${{ steps.load_secrets.outputs.SECRET }}
|
||||||
|
SECRET_IN_SECTION: ${{ steps.load_secrets.outputs.SECRET_IN_SECTION }}
|
||||||
|
MULTILINE_SECRET: ${{ steps.load_secrets.outputs.MULTILINE_SECRET }}
|
||||||
|
run: ./tests/assert-env-set.sh
|
||||||
|
run-on-macos-12:
|
||||||
|
runs-on: macos-12
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Load secrets
|
||||||
|
id: load_secrets
|
||||||
|
uses: ./ # 1password/load-secrets-action@<version>
|
||||||
|
env:
|
||||||
|
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
|
||||||
|
SECRET: op://acceptance-tests/test-secret/password
|
||||||
|
SECRET_IN_SECTION: op://acceptance-tests/test-secret/test-section/password
|
||||||
|
MULTILINE_SECRET: op://acceptance-tests/multiline-secret/notesPlain
|
||||||
|
- name: Assert test secret values
|
||||||
|
env:
|
||||||
|
SECRET: ${{ steps.load_secrets.outputs.SECRET }}
|
||||||
|
SECRET_IN_SECTION: ${{ steps.load_secrets.outputs.SECRET_IN_SECTION }}
|
||||||
|
MULTILINE_SECRET: ${{ steps.load_secrets.outputs.MULTILINE_SECRET }}
|
||||||
|
run: ./tests/assert-env-set.sh
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1 @@
|
|||||||
coverage/
|
|
||||||
node_modules/
|
node_modules/
|
||||||
|
|||||||
100
README.md
100
README.md
@@ -1,27 +1,38 @@
|
|||||||
<!-- Image sourced from https://blog.1password.com/1password-service-accounts/ -->
|
# Load Secrets from 1Password - GitHub Action
|
||||||
<img alt="" role="img" src="https://blog.1password.com/posts/2023/1password-service-accounts/header.png"/>
|
|
||||||
|
|
||||||
<div align="center">
|
`load-secrets-action` loads secrets from 1Password into GitHub Actions using [1Password Connect](https://developer.1password.com/docs/connect).
|
||||||
<h1>Load Secrets from 1Password - GitHub Action</h1>
|
|
||||||
<p>Provide the secrets your GitHub runner needs from 1Password.</p>
|
|
||||||
<a href="https://developer.1password.com/docs/ci-cd/github-actions">
|
|
||||||
<img alt="Get started" src="https://user-images.githubusercontent.com/45081667/226940040-16d3684b-60f4-4d95-adb2-5757a8f1bc15.png" height="37"/>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
`load-secrets-action` loads secrets from 1Password into GitHub Actions using [Service Accounts](https://developer.1password.com/docs/service-accounts) or [1Password Connect](https://developer.1password.com/docs/connect).
|
|
||||||
|
|
||||||
Specify in your workflow YAML file which secrets from 1Password should be loaded into your job, and the action will make them available as environment variables for the next steps.
|
Specify in your workflow YAML file which secrets from 1Password should be loaded into your job, and the action will make them available as environment variables for the next steps.
|
||||||
|
|
||||||
Read more on the [1Password Developer Portal](https://developer.1password.com/docs/ci-cd/github-actions).
|
## Requirements
|
||||||
|
|
||||||
## 🪄 See it in action!
|
Before you get started, you'll need to:
|
||||||
|
|
||||||
[](https://www.youtube.com/watch?v=kVBl5iQYgSA "Using 1Password Service Accounts with GitHub Actions")
|
- [Deploy 1Password Connect](https://developer.1password.com/docs/connect/get-started#step-2-deploy-1password-connect-server) in your infrastructure.
|
||||||
|
- Set the `OP_CONNECT_HOST` and `OP_CONNECT_TOKEN` environment variables to your Connect instance's credentials, so it'll be used to load secrets.
|
||||||
|
|
||||||
## ✨ Quickstart
|
### Supported runners
|
||||||
|
|
||||||
|
You can run the action on Mac and Linux runners. Windows is currently not supported.
|
||||||
|
|
||||||
|
## 1Password configuration
|
||||||
|
|
||||||
|
By default, you'll need to set the environment variables for your Connect instance in the step that uses `load-secrets-action`.
|
||||||
|
|
||||||
|
If you're using the action more than once in a single job, [you can use the `configure` action](https://developer.1password.com/docs/connect/github-actions/#1password-configuration) to set the environment variables instead, so you don't have to set them separately in each `load-secrets-action` step.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
You can load secrets using the action in two ways:
|
||||||
|
|
||||||
|
1. [Use secrets from the action's output](#use-secrets-from-the-actions-output)
|
||||||
|
2. [Export secrets as environment variables](#export-secrets-as-environment-variables)
|
||||||
|
|
||||||
|
### Use secrets from the action's output
|
||||||
|
|
||||||
|
This method allows you to use the loaded secrets outputted by the step: `steps.step-id.outputs.secret-name`.
|
||||||
|
|
||||||
|
You'll need to set an ID for the step to be able to access its outputs. For more information, see [`outputs.<output_id>`](https://docs.github.com/actions/creating-actions/metadata-syntax-for-github-actions#outputsoutput_id).
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
on: push
|
on: push
|
||||||
@@ -29,7 +40,34 @@ jobs:
|
|||||||
hello-world:
|
hello-world:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Load secret
|
||||||
|
id: op-load-secret
|
||||||
|
uses: 1password/load-secrets-action@v1
|
||||||
|
env:
|
||||||
|
OP_CONNECT_HOST: <Your Connect instance URL>
|
||||||
|
OP_CONNECT_TOKEN: ${{ secrets.OP_CONNECT_TOKEN }}
|
||||||
|
SECRET: op://app-cicd/hello-world/secret
|
||||||
|
|
||||||
|
- name: Print masked secret
|
||||||
|
run: echo "Secret: ${{ steps.op-load-secret.outputs.SECRET }}"
|
||||||
|
# Prints: Secret: ***
|
||||||
|
```
|
||||||
|
|
||||||
|
[Read the full documentation for more usage examples.](https://developer.1password.com/docs/connect/github-actions/#use-secrets-from-the-actions-output)
|
||||||
|
|
||||||
|
### Export secrets as environment variables
|
||||||
|
|
||||||
|
This method allows the action to access the loaded secrets as environment variables. These environment variables are accessible at a job level.
|
||||||
|
|
||||||
|
```yml
|
||||||
|
on: push
|
||||||
|
jobs:
|
||||||
|
hello-world:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Load secret
|
- name: Load secret
|
||||||
uses: 1password/load-secrets-action@v1
|
uses: 1password/load-secrets-action@v1
|
||||||
@@ -37,24 +75,32 @@ jobs:
|
|||||||
# Export loaded secrets as environment variables
|
# Export loaded secrets as environment variables
|
||||||
export-env: true
|
export-env: true
|
||||||
env:
|
env:
|
||||||
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
|
OP_CONNECT_HOST: <Your Connect instance URL>
|
||||||
|
OP_CONNECT_TOKEN: ${{ secrets.OP_CONNECT_TOKEN }}
|
||||||
SECRET: op://app-cicd/hello-world/secret
|
SECRET: op://app-cicd/hello-world/secret
|
||||||
|
|
||||||
- name: Print masked secret
|
- name: Print masked secret
|
||||||
run: 'echo "Secret: $SECRET"'
|
run: echo "Secret: $SECRET"
|
||||||
# Prints: Secret: ***
|
# Prints: Secret: ***
|
||||||
```
|
```
|
||||||
|
|
||||||
## 💙 Community & Support
|
[Read the full documentation for more usage examples.](https://developer.1password.com/docs/connect/github-actions/#export-secrets-as-environment-variables)
|
||||||
|
|
||||||
- File an [issue](https://github.com/1Password/load-secrets-action/issues) for bugs and feature requests.
|
## Masking
|
||||||
- Join the [Developer Slack workspace](https://join.slack.com/t/1password-devs/shared_invite/zt-1halo11ps-6o9pEv96xZ3LtX_VE0fJQA).
|
|
||||||
- Subscribe to the [Developer Newsletter](https://1password.com/dev-subscribe/).
|
|
||||||
|
|
||||||
## 🔐 Security
|
Similar to regular GitHub repository secrets, fields from 1Password will automatically be masked in GitHub Actions logs. If one of these values accidentally gets printed, it'll be replaced with `***`.
|
||||||
|
|
||||||
|
## Security
|
||||||
|
|
||||||
1Password requests you practice responsible disclosure if you discover a vulnerability.
|
1Password requests you practice responsible disclosure if you discover a vulnerability.
|
||||||
|
|
||||||
Please file requests via [**BugCrowd**](https://bugcrowd.com/agilebits).
|
Please file requests through [BugCrowd](https://bugcrowd.com/agilebits).
|
||||||
|
|
||||||
|
[Learn more about our security practices.](https://bugcrowd.com/agilebits)
|
||||||
|
|
||||||
|
## Get help
|
||||||
|
|
||||||
|
If you find yourself stuck, [contact 1Password support](https://support.1password.com/) for help.
|
||||||
|
|
||||||
|
[Read the full documentation](https://developer.1password.com/docs/connect/github-actions/).
|
||||||
|
|
||||||
For information about security practices, please visit the [1Password Bug Bounty Program](https://bugcrowd.com/agilebits).
|
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ branding:
|
|||||||
inputs:
|
inputs:
|
||||||
unset-previous:
|
unset-previous:
|
||||||
description: Whether to unset environment variables populated by 1Password in earlier job steps
|
description: Whether to unset environment variables populated by 1Password in earlier job steps
|
||||||
default: "false"
|
default: false
|
||||||
export-env:
|
export-env:
|
||||||
description: Export the secrets as environment variables
|
description: Export the secrets as environment variables
|
||||||
default: "true"
|
default: false
|
||||||
runs:
|
runs:
|
||||||
using: "node16"
|
using: 'node16'
|
||||||
main: "dist/index.js"
|
main: 'dist/index.js'
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
#!/usr/bin/env sh
|
|
||||||
. "$(dirname -- "$0")/_/husky.sh"
|
|
||||||
|
|
||||||
npx lint-staged --config ./config/lint-staged.config.js
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
#!/usr/bin/env sh
|
|
||||||
. "$(dirname -- "$0")/_/husky.sh"
|
|
||||||
|
|
||||||
npm run validate
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
coverage/
|
|
||||||
dist/
|
|
||||||
node_modules/
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
const jestConfig = {
|
|
||||||
/**
|
|
||||||
* Jest docs: "We recommend placing the extensions most commonly used in your project
|
|
||||||
* on the left, so if you are using TypeScript, you may want to consider
|
|
||||||
* moving 'ts' to the beginning of the array."
|
|
||||||
*
|
|
||||||
* https://jestjs.io/docs/configuration#modulefileextensions-arraystring
|
|
||||||
*/
|
|
||||||
moduleFileExtensions: ["ts", "js", "json"],
|
|
||||||
rootDir: "../src/",
|
|
||||||
testEnvironment: "node",
|
|
||||||
testRegex: "(/__tests__/.*|(\\.|/)test)\\.ts",
|
|
||||||
transform: {
|
|
||||||
".ts": ["ts-jest"],
|
|
||||||
},
|
|
||||||
verbose: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default jestConfig;
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
const lintStagedConfig = {
|
|
||||||
// run formatting and linting on all supported file types
|
|
||||||
"*.{js,json,md,ts,yaml,yml}": "npm run format:write",
|
|
||||||
"*.{js,ts}": ["npm run lint:fix"],
|
|
||||||
// run testing on all supported file types within the src/ directory
|
|
||||||
"src/**/*.{js,ts}": ["npm run test -- --findRelatedTests"],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default lintStagedConfig;
|
|
||||||
25557
dist/index.js
vendored
25557
dist/index.js
vendored
File diff suppressed because one or more lines are too long
3
dist/package.json
vendored
3
dist/package.json
vendored
@@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"type": "module"
|
|
||||||
}
|
|
||||||
@@ -2,11 +2,6 @@
|
|||||||
# shellcheck disable=SC2046,SC2001,SC2086
|
# shellcheck disable=SC2046,SC2001,SC2086
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# Pass User-Agent Inforomation to the 1Password CLI
|
|
||||||
export OP_INTEGRATION_NAME="1Password GitHub Action"
|
|
||||||
export OP_INTEGRATION_ID="GHA"
|
|
||||||
export OP_INTEGRATION_BUILDNUMBER="1010001"
|
|
||||||
|
|
||||||
readonly CONNECT="CONNECT"
|
readonly CONNECT="CONNECT"
|
||||||
readonly SERVICE_ACCOUNT="SERVICE_ACCOUNT"
|
readonly SERVICE_ACCOUNT="SERVICE_ACCOUNT"
|
||||||
|
|
||||||
@@ -14,10 +9,6 @@ auth_type=$CONNECT
|
|||||||
managed_variables_var="OP_MANAGED_VARIABLES"
|
managed_variables_var="OP_MANAGED_VARIABLES"
|
||||||
IFS=','
|
IFS=','
|
||||||
|
|
||||||
if [[ "$OP_CONNECT_HOST" != "http://"* ]] && [[ "$OP_CONNECT_HOST" != "https://"* ]]; then
|
|
||||||
export OP_CONNECT_HOST="http://"$OP_CONNECT_HOST
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Unset all secrets managed by 1Password if `unset-previous` is set.
|
# Unset all secrets managed by 1Password if `unset-previous` is set.
|
||||||
unset_prev_secrets() {
|
unset_prev_secrets() {
|
||||||
if [ "$INPUT_UNSET_PREVIOUS" == "true" ]; then
|
if [ "$INPUT_UNSET_PREVIOUS" == "true" ]; then
|
||||||
@@ -39,51 +30,12 @@ unset_prev_secrets() {
|
|||||||
|
|
||||||
# Install op-cli
|
# Install op-cli
|
||||||
install_op_cli() {
|
install_op_cli() {
|
||||||
# Create a temporary directory where the CLI is installed
|
|
||||||
OP_INSTALL_DIR="$(mktemp -d)"
|
|
||||||
if [[ ! -d "$OP_INSTALL_DIR" ]]; then
|
|
||||||
echo "Install dir $OP_INSTALL_DIR not found"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
export OP_INSTALL_DIR
|
|
||||||
echo "::debug::OP_INSTALL_DIR: ${OP_INSTALL_DIR}"
|
|
||||||
|
|
||||||
# Get the latest stable version of the CLI
|
|
||||||
OP_CLI_VERSION="v$(curl https://app-updates.agilebits.com/check/1/0/CLI2/en/2.0.0/N -s | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+')"
|
|
||||||
|
|
||||||
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
||||||
# Get runner's architecture
|
curl -sSfLo op.zip "https://cache.agilebits.com/dist/1P/op2/pkg/v2.7.1-beta.01/op_linux_amd64_v2.7.1-beta.01.zip"
|
||||||
ARCH=$(uname -m)
|
unzip -od /usr/local/bin/ op.zip && rm op.zip
|
||||||
if [[ "$(getconf LONG_BIT)" = 32 ]]; then
|
|
||||||
ARCH="386"
|
|
||||||
elif [[ "$ARCH" == "x86_64" ]]; then
|
|
||||||
ARCH="amd64"
|
|
||||||
elif [[ "$ARCH" == "aarch64" ]]; then
|
|
||||||
ARCH="arm64"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$ARCH" != "386" ]] && [[ "$ARCH" != "amd64" ]] && [[ "$ARCH" != "arm" ]] && [[ "$ARCH" != "arm64" ]]; then
|
|
||||||
echo "Unsupported architecture for the 1Password CLI: $ARCH."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
curl -sSfLo op.zip "https://cache.agilebits.com/dist/1P/op2/pkg/${OP_CLI_VERSION}/op_linux_${ARCH}_${OP_CLI_VERSION}.zip"
|
|
||||||
unzip -od "$OP_INSTALL_DIR" op.zip && rm op.zip
|
|
||||||
elif [[ "$OSTYPE" == "darwin"* ]]; then
|
elif [[ "$OSTYPE" == "darwin"* ]]; then
|
||||||
curl -sSfLo op.pkg "https://cache.agilebits.com/dist/1P/op2/pkg/${OP_CLI_VERSION}/op_apple_universal_${OP_CLI_VERSION}.pkg"
|
curl -sSfLo op.pkg "https://cache.agilebits.com/dist/1P/op2/pkg/v2.7.1-beta.01/op_apple_universal_v2.7.1-beta.01.pkg"
|
||||||
pkgutil --expand op.pkg temp-pkg
|
sudo installer -pkg op.pkg -target /usr/local/bin/ && rm op.pkg
|
||||||
tar -xvf temp-pkg/op.pkg/Payload -C "$OP_INSTALL_DIR"
|
|
||||||
rm -rf temp-pkg && rm op.pkg
|
|
||||||
else
|
|
||||||
echo "Operating system not supported yet for this GitHub Action: $OSTYPE."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Uninstall op-cli
|
|
||||||
uninstall_op_cli() {
|
|
||||||
if [[ -d "$OP_INSTALL_DIR" ]]; then
|
|
||||||
rm -fr "$OP_INSTALL_DIR"
|
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,7 +43,7 @@ populating_secret() {
|
|||||||
ref=$(printenv $1)
|
ref=$(printenv $1)
|
||||||
|
|
||||||
echo "Populating variable: $1"
|
echo "Populating variable: $1"
|
||||||
secret_value=$("${OP_INSTALL_DIR}/op" read "$ref")
|
secret_value=$(op read $ref)
|
||||||
|
|
||||||
if [ -z "$secret_value" ]; then
|
if [ -z "$secret_value" ]; then
|
||||||
echo "Could not find or access secret $ref"
|
echo "Could not find or access secret $ref"
|
||||||
@@ -111,28 +63,25 @@ populating_secret() {
|
|||||||
done
|
done
|
||||||
unset IFS
|
unset IFS
|
||||||
|
|
||||||
# To support multiline secrets, we'll use the heredoc syntax to populate the environment variables.
|
|
||||||
# As the heredoc identifier, we'll use a randomly generated 64-character string,
|
|
||||||
# so that collisions are practically impossible.
|
|
||||||
# Read more: https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#multiline-strings
|
|
||||||
delimiter="$(openssl rand -hex 32)"
|
|
||||||
|
|
||||||
if [ "$INPUT_EXPORT_ENV" == "true" ]; then
|
if [ "$INPUT_EXPORT_ENV" == "true" ]; then
|
||||||
|
# To support multiline secrets, we'll use the heredoc syntax to populate the environment variables.
|
||||||
|
# As the heredoc identifier, we'll use a randomly generated 64-character string,
|
||||||
|
# so that collisions are practically impossible.
|
||||||
|
random_heredoc_identifier=$(openssl rand -hex 16)
|
||||||
|
|
||||||
{
|
{
|
||||||
# Populate env var, using heredoc syntax with generated identifier
|
# Populate env var, using heredoc syntax with generated identifier
|
||||||
echo "$env_var<<${delimiter}"
|
echo "$env_var<<${random_heredoc_identifier}"
|
||||||
echo "$secret_value"
|
echo "$secret_value"
|
||||||
echo "${delimiter}"
|
echo "${random_heredoc_identifier}"
|
||||||
} >> $GITHUB_ENV
|
} >> $GITHUB_ENV
|
||||||
echo "GITHUB_ENV: $(cat $GITHUB_ENV)"
|
echo "GITHUB_ENV: $(cat $GITHUB_ENV)"
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
# Prepare the secret_value to be outputed properly (especially multiline secrets)
|
||||||
# Populate env var, using heredoc syntax with generated identifier
|
secret_value=$(echo "$secret_value" | awk -v ORS='%0A' '1')
|
||||||
echo "$env_var<<${delimiter}"
|
|
||||||
echo "$secret_value"
|
echo "::set-output name=$env_var::$secret_value"
|
||||||
echo "${delimiter}"
|
|
||||||
} >> $GITHUB_OUTPUT
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
managed_variables+=("$env_var")
|
managed_variables+=("$env_var")
|
||||||
@@ -142,7 +91,7 @@ populating_secret() {
|
|||||||
# and make them available as environment variables in the next steps.
|
# and make them available as environment variables in the next steps.
|
||||||
extract_secrets() {
|
extract_secrets() {
|
||||||
IFS=$'\n'
|
IFS=$'\n'
|
||||||
for env_var in $("${OP_INSTALL_DIR}/op" env ls); do
|
for env_var in $(op env ls); do
|
||||||
populating_secret $env_var
|
populating_secret $env_var
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
@@ -163,7 +112,6 @@ printf "Authenticated with %s \n" $auth_type
|
|||||||
unset_prev_secrets
|
unset_prev_secrets
|
||||||
install_op_cli
|
install_op_cli
|
||||||
extract_secrets
|
extract_secrets
|
||||||
uninstall_op_cli
|
|
||||||
|
|
||||||
unset IFS
|
unset IFS
|
||||||
# Add extra env var that lists which secrets are managed by 1Password so that in a later step
|
# Add extra env var that lists which secrets are managed by 1Password so that in a later step
|
||||||
|
|||||||
8757
package-lock.json
generated
8757
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
99
package.json
99
package.json
@@ -1,66 +1,37 @@
|
|||||||
{
|
{
|
||||||
"name": "load-secrets-action",
|
"name": "load-secrets-action",
|
||||||
"version": "1.3.2",
|
"version": "1.1.0",
|
||||||
"description": "Load Secrets from 1Password",
|
"description": "Load Secrets from 1Password",
|
||||||
"type": "module",
|
"main": "dist/index.js",
|
||||||
"main": "dist/index.js",
|
"directories": {
|
||||||
"directories": {
|
"test": "tests"
|
||||||
"test": "tests"
|
},
|
||||||
},
|
"scripts": {
|
||||||
"scripts": {
|
"build": "ncc build src/index.ts"
|
||||||
"build": "ncc build ./src/index.ts",
|
},
|
||||||
"format": "prettier --ignore-path ./config/.prettierignore",
|
"repository": {
|
||||||
"format:check": "npm run format -- --check ./",
|
"type": "git",
|
||||||
"format:write": "npm run format -- --write ./",
|
"url": "git+https://github.com/1Password/load-secrets-action.git"
|
||||||
"lint": "eslint ./",
|
},
|
||||||
"lint:fix": "npm run lint -- --fix",
|
"keywords": [
|
||||||
"prepare": "husky install ./config/.husky",
|
"actions",
|
||||||
"test": "jest --config=./config/jest.config.js",
|
"1password",
|
||||||
"test:clearcache": "jest --clearCache",
|
"load secrets",
|
||||||
"test:coverage": "npm run test -- --coverage",
|
"connect"
|
||||||
"test:watch": "npm run test -- --watch",
|
],
|
||||||
"typecheck": "tsc",
|
"author": "1Password",
|
||||||
"validate": "npm run format:check && npm run lint && npm run test:coverage && npm run typecheck && npm run build"
|
"license": "MIT",
|
||||||
},
|
"bugs": {
|
||||||
"repository": {
|
"url": "https://github.com/1Password/load-secrets-action/issues"
|
||||||
"type": "git",
|
},
|
||||||
"url": "git+https://github.com/1Password/load-secrets-action.git"
|
"homepage": "https://github.com/1Password/load-secrets-action#readme",
|
||||||
},
|
"dependencies": {
|
||||||
"keywords": [
|
"@actions/core": "^1.9.1",
|
||||||
"actions",
|
"@actions/exec": "^1.1.1"
|
||||||
"1password",
|
},
|
||||||
"load secrets",
|
"devDependencies": {
|
||||||
"connect"
|
"@types/node": "^18.7.14",
|
||||||
],
|
"@vercel/ncc": "^0.34.0",
|
||||||
"author": "1Password",
|
"typescript": "^4.8.2"
|
||||||
"license": "MIT",
|
}
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/1Password/load-secrets-action/issues"
|
|
||||||
},
|
|
||||||
"homepage": "https://github.com/1Password/load-secrets-action#readme",
|
|
||||||
"dependencies": {
|
|
||||||
"@actions/core": "^1.10.1",
|
|
||||||
"@actions/exec": "^1.1.1"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@1password/front-end-style": "^6.0.1",
|
|
||||||
"@types/jest": "^29.5.6",
|
|
||||||
"@types/node": "^18.18.6",
|
|
||||||
"@vercel/ncc": "^0.36.1",
|
|
||||||
"husky": "^8.0.3",
|
|
||||||
"jest": "^29.7.0",
|
|
||||||
"lint-staged": "^13.3.0",
|
|
||||||
"ts-jest": "^29.1.1",
|
|
||||||
"typescript": "^4.9.5"
|
|
||||||
},
|
|
||||||
"eslintConfig": {
|
|
||||||
"extends": "./node_modules/@1password/front-end-style/eslintrc.yml",
|
|
||||||
"ignorePatterns": [
|
|
||||||
"coverage/"
|
|
||||||
],
|
|
||||||
"parserOptions": {
|
|
||||||
"project": "./tsconfig.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"prettier": "./node_modules/@1password/front-end-style/prettierrc.json"
|
|
||||||
}
|
}
|
||||||
|
|||||||
45
src/index.ts
45
src/index.ts
@@ -1,32 +1,21 @@
|
|||||||
import path from "path";
|
import * as core from '@actions/core';
|
||||||
import url from "url";
|
import * as exec from '@actions/exec';
|
||||||
import * as core from "@actions/core";
|
import path from 'path';
|
||||||
import * as exec from "@actions/exec";
|
|
||||||
|
|
||||||
const run = async () => {
|
async function run(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const currentFile = url.fileURLToPath(import.meta.url);
|
const parentDir = path.resolve(__dirname, '..');
|
||||||
const currentDir = path.dirname(currentFile);
|
|
||||||
const parentDir = path.resolve(currentDir, "..");
|
// Get action inputs
|
||||||
|
process.env.INPUT_UNSET_PREVIOUS = core.getInput('unset-previous');
|
||||||
|
process.env.INPUT_EXPORT_ENV = core.getInput('export-env');
|
||||||
|
|
||||||
// Get action inputs
|
// Execute bash script
|
||||||
process.env.INPUT_UNSET_PREVIOUS = core.getInput("unset-previous");
|
await exec.exec(`sh -c "` + parentDir + `/entrypoint.sh"`);
|
||||||
process.env.INPUT_EXPORT_ENV = core.getInput("export-env");
|
|
||||||
|
|
||||||
// Execute bash script
|
} catch (error: any) {
|
||||||
await exec.exec(`sh -c "` + parentDir + `/entrypoint.sh"`);
|
core.setFailed(error.message);
|
||||||
} catch (error) {
|
}
|
||||||
// It's possible for the Error constructor to be modified to be anything
|
}
|
||||||
// in JavaScript, so the following code accounts for this possibility.
|
|
||||||
// https://kentcdodds.com/blog/get-a-catch-block-error-message-with-typescript
|
|
||||||
let message = "Unknown Error";
|
|
||||||
if (error instanceof Error) {
|
|
||||||
message = error.message;
|
|
||||||
} else {
|
|
||||||
String(error);
|
|
||||||
}
|
|
||||||
core.setFailed(message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void run();
|
run();
|
||||||
|
|||||||
@@ -1,25 +1,13 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"allowJs": false,
|
"target": "es6",
|
||||||
"allowUnreachableCode": false,
|
"module": "commonjs",
|
||||||
"allowUnusedLabels": false,
|
"moduleResolution": "node",
|
||||||
"esModuleInterop": true,
|
"outDir": "./dist",
|
||||||
"exactOptionalPropertyTypes": true,
|
"rootDir": "./src",
|
||||||
"forceConsistentCasingInFileNames": true,
|
"strict": true,
|
||||||
"importsNotUsedAsValues": "error",
|
"noImplicitAny": true,
|
||||||
"isolatedModules": true,
|
"esModuleInterop": true
|
||||||
"module": "esnext",
|
},
|
||||||
"moduleResolution": "node",
|
"exclude": ["node_modules"]
|
||||||
"noEmit": true,
|
|
||||||
"noEmitOnError": true,
|
|
||||||
"noFallthroughCasesInSwitch": true,
|
|
||||||
"noImplicitReturns": true,
|
|
||||||
"noUncheckedIndexedAccess": true,
|
|
||||||
"noUnusedLocals": true,
|
|
||||||
"noUnusedParameters": true,
|
|
||||||
"outDir": "./dist/",
|
|
||||||
"rootDir": "./src/",
|
|
||||||
"strict": true,
|
|
||||||
"target": "es2022"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user