Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 15 additions & 32 deletions .github/actions/fetch-secrets/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,14 @@ runs:
using: composite
steps:
- name: Register role ARN mask
shell: bash
run: echo "::add-mask::${{ inputs.role-arn }}"
shell: node {0}
env:
ACTION_PATH: ${{ github.action_path }}
ROLE_ARN: ${{ inputs.role-arn }}
run: |
const path = require("node:path");
const { registerMask } = require(path.join(process.env.ACTION_PATH, "build-secret-mappings.js"));
registerMask(process.env.ROLE_ARN);

- name: Configure AWS credentials (central secrets reader)
uses: aws-actions/configure-aws-credentials@v6
Expand All @@ -42,44 +48,21 @@ runs:
aws-region: ${{ inputs.aws-region }}
mask-aws-account-id: true

- name: Build secret-ids list
- name: Build secret mappings
id: build
shell: bash
shell: node {0}
env:
ACTION_PATH: ${{ github.action_path }}
SHARED_NAMES: ${{ inputs.shared }}
REPO_NAMES: ${{ inputs.repo }}
CALLER_REPO: ${{ github.repository }}
run: |
set -euo pipefail
# Normalize commas/spaces to newlines, drop blanks; build "<NAME>,<folder>/<NAME>".
# Alias = the bare NAME, so each secret lands in an env var of that exact name.
: > /tmp/secret-ids.txt
emit() {
local prefix="$1" names="$2"
echo "$names" | tr ', ' '\n\n' | while IFS= read -r n; do
n="$(echo "$n" | xargs)" # trim surrounding whitespace
[ -z "$n" ] && continue
echo "${n},${prefix}/${n}" >> /tmp/secret-ids.txt
done
}
emit "shared" "$SHARED_NAMES"
emit "$CALLER_REPO" "$REPO_NAMES"

if [ ! -s /tmp/secret-ids.txt ]; then
echo "::error::fetch-secrets: neither 'shared' nor 'repo' provided any secret names."
exit 1
fi

echo "Resolved secret-ids:"
sed 's/^/ /' /tmp/secret-ids.txt
{
echo "ids<<SECRET_IDS_EOF"
cat /tmp/secret-ids.txt
echo "SECRET_IDS_EOF"
} >> "$GITHUB_OUTPUT"
const path = require("node:path");
const { main } = require(path.join(process.env.ACTION_PATH, "build-secret-mappings.js"));
main();

- name: Fetch secrets
uses: aws-actions/aws-secretsmanager-get-secrets@v2
with:
secret-ids: ${{ steps.build.outputs.ids }}
secret-ids: ${{ steps.build.outputs.mappings }}
parse-json-secrets: false
61 changes: 61 additions & 0 deletions .github/actions/fetch-secrets/build-secret-mappings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
const fs = require("node:fs");

const OUTPUT_DELIMITER = "SECRET_MAPPINGS_EOF";

/**
* Registers a value for redaction from subsequent GitHub Actions logs.
*
* @param value - Sensitive value to mask.
* @see {@link https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#masking-a-value-in-a-log}
*/
function registerMask(value) {
console.log(`::add-mask::${value}`);
}

/**
* Resolves action inputs and appends the multiline secret ID step output.
*
* @param environment - GitHub Actions environment variables.
* @see {@link https://github.com/aws-actions/aws-secretsmanager-get-secrets}
* @see {@link https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-output-parameter}
*/
function main(environment = process.env) {
// Format each name as "<alias>,<prefix>/<name>".
const formatMappings = (names = "", prefix) =>
names
.split(/[,\s]+/)
.map((name) => name.trim())
.filter(Boolean)
.map((name) => `${name},${prefix}/${name}`);

const secretMappings = [
...formatMappings(environment.SHARED_NAMES, "shared"),
...formatMappings(environment.REPO_NAMES, environment.CALLER_REPO),
];

if (secretMappings.length === 0) {
console.error(
"::error::fetch-secrets: neither 'shared' nor 'repo' provided any secret names.",
);
process.exitCode = 1;
return;
}

console.log(
`Resolved secret mappings:\n${secretMappings.map((mapping) => ` ${mapping}`).join("\n")}`,
);

fs.appendFileSync(
environment.GITHUB_OUTPUT,
`mappings<<${OUTPUT_DELIMITER}\n${secretMappings.join("\n")}\n${OUTPUT_DELIMITER}\n`,
);
}

if (require.main === module) {
main();
}

module.exports = {
main,
registerMask,
};