Skip to content
Open
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
76 changes: 60 additions & 16 deletions .github/workflows/org-invite-codeheat.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,22 @@ jobs:
TEAM_SLUG: "codeheat"

steps:
- name: Debug secret presence
env:
ORG_ADMIN_TOKEN: ${{ secrets.ORG_ADMIN_TOKEN }}
run: |
if [ -z "${ORG_ADMIN_TOKEN}" ]; then
echo "❌ ORG_ADMIN_TOKEN is NOT available"
exit 1
else
echo "✅ ORG_ADMIN_TOKEN is available"
fi

- name: Invite user to org and team
uses: actions/github-script@v7
with:
github-token: ${{ secrets.ORG_ADMIN_TOKEN }}
script: |
// Validate that the token is present
if (!github.token || github.token.trim() === "") {
throw new Error("ORG_ADMIN_TOKEN is missing or empty. Please set the required secret.");
}
const org = process.env.ORG_NAME;
const team_slug = process.env.TEAM_SLUG;
const issue = context.payload.issue;
Expand Down Expand Up @@ -71,18 +78,40 @@ jobs:

// ---- Org membership ----
let membership;
let invited = false;

try {
membership = await github.rest.orgs.setMembershipForUser({
membership = await github.rest.orgs.getMembershipForUser({
org,
username
username,
});
} catch (err) {
await comment(
`❌ **Failed to invite @${username} to ${org}**\n\n` +
`Error: ${err.message}`
);
core.setFailed(err.message);
return;
} catch (error) {
if (error.status !== 404) {
await comment(
`❌ **Onboarding failed**\n\n` +
`Could not retrieve organization membership for \`@${username}\` in \`${org}\`.\n` +
`Error: \`${error.message}\``
);
core.setFailed(error.message);
return;
}

// User not found, attempt to invite
try {
membership = await github.rest.orgs.setMembershipForUser({
org,
username,
});
invited = true;
} catch (inviteError) {
await comment(
`❌ **Onboarding failed**\n\n` +
`Could not invite \`@${username}\` to organization \`${org}\`.\n` +
`Error: \`${inviteError.message}\``
);
core.setFailed(inviteError.message);
return;
}
}

// ---- Team membership (non-fatal) ----
Expand All @@ -99,15 +128,30 @@ jobs:
);
}

const orgStatusLine = invited
? `• Org invitation: **${membership.data.state}**\n`
: `• Org membership: **already active**\n`;
Comment on lines +131 to +133
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Org membership text can be incorrect for existing but pending members.

When getMembershipForUser returns state === 'pending' for an existing membership, invited stays false, so orgStatusLine becomes "• Org membership: **already active**", even though the later actionMessage logic correctly treats this as pending. Please derive orgStatusLine from membership.data.state in both cases (or at least special-case pending when invited === false) so the status text matches the actual membership state.


const teamStatusLine = teamMembership
? `• Team membership: **${teamMembership.data.state}**\n`
: `• Team membership: ❌ **failed** - please add manually to \`${team_slug}\`\n`;

let actionMessage = '';

if (membership.data.state === 'pending') {
actionMessage =
`\nℹ️ Please accept the GitHub organization invitation ` +
`from your notifications or email to complete onboarding.`;
} else {
actionMessage =
`\nℹ️ You are already an active member. No further action is required.`;
}

await comment(
`✅ **Onboarding processed for @${username}**\n\n` +
`• Org membership: **${membership.data.state}**\n` +
`✅ **Onboarding request processed for @${username}**\n\n` +
orgStatusLine +
teamStatusLine +
`\nIf any status is **pending**, please accept the GitHub invitation.`
actionMessage
);

// ---- Close issue (non-fatal) ----
Expand Down